1 文章目的

昨天写了走进WebApiClientCore的设计,介绍了WebApiClient的变化与设计之后,收到大家支持的、赞许的,还有好的建议和顾虑,比如WebApiClient性能怎么样,有没有一些对比参考值?我一直有个不好毛病,凭直接感观代码的运行效率,直觉里WebApiClient的代码都是优化的,就算性能不好,也没有更好的性能弥补方案!但真要这样和使用这个库的开发者说,相信要一秒内被打屎的,所以今天使用了BenchmarkDotNet对WebApiClient.JIT最新稳定版、WebApiClientCore预览版和原生的HttpClient做了请求和响应处理耗时对比。

2 排除网络IO等待干扰

我们必须要想办法去掉真实网络请求的时间干扰,因为一个请求回合中,网络等待时间可能会占用到整个流程的99.99%以上的时间。最好的办法就是没有请求,而是模拟一个响应内容。

System.Net.Http支持管道式包装请求处理对象,叫DelegatingHandler,我们可以自定义一个DelegatingHandler,重写其SendAsync()方法,然后直接返回模拟的响应消息HttpResponseMessage,使用这个DelegatingHandler的HttpClient,在请求时不会真实将数据发送到目标服务器去。

/// <summary>
/// 无真实http请求的Handler
/// </summary>
class NoneHttpDelegatingHandler : DelegatingHandler
{
private readonly HttpResponseMessage benchmarkModelResponseMessage; public NoneHttpDelegatingHandler()
{
var model = new Model { A = "A", B = 2, C = 3d };
var json = JsonSerializer.SerializeToUtf8Bytes(model);
this.benchmarkModelResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) { Content = new JsonContent(json) };
} protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(this.benchmarkModelResponseMessage);
}
}

3 Benchmark测试

3.1 注册到DI

为了公平,三种对比对象都注册到到DI里,使用时从DI获取实例

var services = new ServiceCollection();

// 原生HttpClient
services
.AddHttpClient(typeof(HttpClient).FullName)
.AddHttpMessageHandler(() => new NoneHttpDelegatingHandler()); // WebApiClientCore
services
.AddHttpApi<IWebApiClientCoreApi>(o => o.HttpHost = new Uri("http://webapiclient.com/"))
.AddHttpMessageHandler(() => new NoneHttpDelegatingHandler()); // WebApiClient
WebApiClient.Extension
.AddHttpApi<IWebApiClientApi>(services, o => o.HttpHost = new Uri("http://webapiclient.com/"))
.AddHttpMessageHandler(() => new NoneHttpDelegatingHandler());

3.2 Benchmark方法

我们为三种客户端每个比较项目各写一个Benchmark方法:从DI获取实例,然后发起Get请求,最后将响应的内容反序列化为强模型。

/// <summary>
/// 使用WebApiClient.JIT请求
/// </summary>
/// <returns></returns>
[Benchmark]
public async Task<Model> WebApiClient_GetAsync()
{
using var scope = this.serviceProvider.CreateScope();
var banchmarkApi = scope.ServiceProvider.GetRequiredService<IWebApiClientApi>();
return await banchmarkApi.GetAsyc(id: "id");
} /// <summary>
/// 使用WebApiClientCore请求
/// </summary>
/// <returns></returns>
[Benchmark]
public async Task<Model> WebApiClientCore_GetAsync()
{
using var scope = this.serviceProvider.CreateScope();
var banchmarkApi = scope.ServiceProvider.GetRequiredService<IWebApiClientCoreApi>();
return await banchmarkApi.GetAsyc(id: "id");
} /// <summary>
/// 使用原生HttpClient请求
/// </summary>
/// <returns></returns>
[Benchmark]
public async Task<Model> HttpClient_GetAsync()
{
using var scope = this.serviceProvider.CreateScope();
var httpClient = scope.ServiceProvider.GetRequiredService<IHttpClientFactory>().CreateClient(typeof(HttpClient).FullName); var id = "id";
var request = new HttpRequestMessage(HttpMethod.Get, $"http://webapiclient.com/{id}");
var response = await httpClient.SendAsync(request);
var json = await response.Content.ReadAsByteArrayAsync();
return JsonSerializer.Deserialize<Model>(json);
}

3.3 Benchmark运行与结果

原于代码篇幅,实际中我们还加入了Post请求测试,所以最后的结果如下:

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.836 (1909/November2018Update/19H2)
Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.201
[Host] : .NET Core 3.1.3 (CoreCLR 4.700.20.11803, CoreFX 4.700.20.12001), X64 RyuJIT
DefaultJob : .NET Core 3.1.3 (CoreCLR 4.700.20.11803, CoreFX 4.700.20.12001), X64 RyuJIT | Method | Mean | Error | StdDev |
|--------------------------- |----------:|----------:|----------:|
| WebApiClient_GetAsync | 25.716 us | 0.5106 us | 0.9838 us |
| WebApiClientCore_GetAsync | 16.047 us | 0.1231 us | 0.0961 us |
| HttpClient_GetAsync | 2.028 us | 0.0240 us | 0.0224 us |
| WebApiClient_PostAsync | 18.712 us | 0.3707 us | 0.3641 us |
| WebApiClientCore_PostAsync | 8.799 us | 0.1696 us | 0.1666 us |
| HttpClient_PostAsync | 3.673 us | 0.0710 us | 0.0972 us |

总结

不管是成熟稳定如狗的WebApiClient,还是新秀WebApiClientCore,其性能和原生的HttpClient在一个数量级,大家放心地使用就是了。

最新文章

  1. jQuery2.x源码解析(回调篇)
  2. Freemarker与普通java
  3. 解决对含有第三方jar包的项目打包出现java.lang.NoClassDefFoundError问题
  4. Android 视频播放器 VideoView 的使用,播放本地视频 和 网络 视频
  5. Spring的事件和监听器
  6. POJ——3984
  7. 问题:关于一个贴友的js留言板的实现
  8. 浅谈 html- table换行
  9. hdu 4790 Just Random (2013成都J题) 数学思路题 容斥
  10. 通过设置cookie实现单点登录
  11. HTML 5最终确定,八年后,我们再谈谈如何改变世界
  12. Do a “git export” (like “svn export”)?(转)
  13. Entity Framework 学习高级篇2—改善EF代码的方法(下)
  14. 《Linux命令行与shell脚本编程大全》 第七章理解Linux文件权限
  15. DirectX11 With Windows SDK--21 鼠标拾取
  16. ubuntu默认的Python版本号修改
  17. js实现图片旋转
  18. ADO.NET 的五个对象
  19. C# 条件表达式max=(a&gt;b)?a:b;含义
  20. spring自动类型转换========Converter和PropertyEditor

热门文章

  1. 前端JS—显示赋值(一)
  2. 2019-2020-1 20199325《Linux内核原理与分析》第七周作业
  3. 2019-2020-1 20199329 第二周测试(环境:ubuntu64位)
  4. 3年前的一个小项目经验,分享给菜鸟兄弟们(公文收发小软件:小技能 SmallDatetime)...
  5. time wait 整理
  6. Geomesa-Hbase集群部署
  7. 信息竞赛进阶指南--KMP算法(模板)
  8. 地表最简单安装MySQL及配置的方法,没有之一
  9. RF(IF 判断)
  10. win7乱码问题解决方法(cmd变小,plsql客户端乱码)