为.netcore助力--WebApiClient正式发布core版本
1 前言
WebApiClient已成熟稳定,发布了WebApiClient.JIT和WebApiClient.AOT两个nuget包,累计近10w次下载。我对它的高可扩展性设计相当满意和自豪,但WebApiClient并不因此而停下脚步,在一年前,我产生了编写其core版本的想法,将asp.netcore服务端先进的思想融入到core版本,在性能与扩展性上得到进一步升华。
对应的,给它叫了WebApiClientCore的名字,为了对得起名字里面的Core字,我在框架设计、性能优化上占用整体开发时间一半以上。
2 框架设计
IActionInvoker
WebApiClient时还没有IActionInvoker概念,对应的执行逻辑直接在ApiActionContext上实现。现在我觉得,Context应该是一个状态数据类,而不能也成为一个执行者,因为一个执行者的实例可以无限次地执行多个Context实例。
Refit则更简单粗暴,将所有实现都在一个RequestBuilderImplementation的类上:你们只要也只能使用我内置的Attribute声明,一切执行在我这个类里面包办,因为我是一个万能类。
Core版本增加了IActionInvoker概念,从中Context分开,用于执行Context,职责分明。在实现上又分为多种Invoker:Task声明返回执行者ActionInvoker、ITask声明返回处理处理者ActionTask,以及聚合的MultiplexedActionInvoker。
Middleware思想
WebApiClient时在处理各个特性、参数验证、返回值验证时没有使用Middleware思想,特别是在处理响应结果和异常短路逻辑难以维护。
Refit还是简单粗暴,将所有特性的解释实现都在这个RequestBuilderImplementation的类上,因为我还是一个万能类。
Core版本增加中间件Builder,将请求前的相关Attribute的执行编排Build为一个请求处理委托,将请求后相关Attribute的执行编排Build为一个响应处理委托,然后把两个委托与真实http请求串在一起,Build出一个完整的请求响应委托。
得益于Middleware,流程中的请求前参数值验证、结果处理特性短路、异常短路、请求后结果值验和无条件执行IApiFilterAtrribue等这些复杂的流程变成简单的管道处理;另外接口也变成支持服务端响应多种格式内容,每种格式内容在一个IApiReturnAttribute上实现和处理,比如请求为Accept: application/json, application/xml
,不管服务器返回xml或json都能处理。
/// <summary>
/// 创建执行委托
/// </summary>
/// <param name="apiAction">action描述器</param>
/// <returns></returns>
public static Func<ApiRequestContext, Task<ApiResponseContext>> Build(ApiActionDescriptor apiAction)
{
var requestHandler = BuildRequestHandler(apiAction);
var responseHandler = BuildResponseHandler(apiAction);
return async request =>
{
await requestHandler(request).ConfigureAwait(false);
var response = await HttpRequest.SendAsync(request).ConfigureAwait(false);
await responseHandler(response).ConfigureAwait(false);
return response;
};
}
Context思想
WebApiClient只有一个ApiActionContext,其Result和Exception属性在请求前就可以访问或设置,但实际上就算设置了值,流程也不会短路和中断,属于设计失误。
Refit没有相关Context概念,因为它不提供给用户自定义扩展Attribute的能力,它内置的Attribute也没有执行能力,一个RequestBuilderImplementation类够了。
Core版本将设计了多个Context概念,不同阶段有不同的Context,如同asp.netcore不同Filter的Context也不同一样。对于一个Action,请求阶段对应是ApiRequestContext,响应阶段是ApiResponseContext;对于Action的参数,对应是ApiParameterContext。每种Context里面都包含核心的HttpContext属性,HttpContext包含请求消息、响应消息和接口配置选项等。
Interface思想
输入WebApiClientCore命名空间,会发现定义了很多Interface,这些Interface都是为了用户实现自定义特性用的,当然内置的所有特性,都是实现了这些接口而已。如果一个特性实现了多个接口,它就有多种能力,比如内置的HeaderAttribute,它既可以修饰于Interface和Method,也可以修饰参数。
WebApiClientCore的Attribute描述的逻辑,是由Attribute自我实现,所以整个请求的数据装配逻辑是分散为各个Attribute上,用什么Attribute就有什么逻辑,包含框架之外的非内置的自定义Attribute。
Refit的内置Attribute只有欲描述逻辑,没有实现逻辑,实现逻辑由RequestBuilderImplementation包办,所以它不需要接口也没有接口。
3 性能优化
更快的字符串替换
像[HttpGet("objects/{id}")]这样的path参数,在restful中常常遇到,通过Span优化,Core版本在替换path参数值cpu占用时间降低为原版本的十分之一。
更快的json序列化
得益于Sytem.Text.Json,json序列化和反序列化上性能显明提升。
更少的缓冲区分配
WebApiClientCore使用了可回收复用的IBufferWriter,在json序列化得到json、json装配为HttpContent只申请一次Buffer,而且HttpContent在发送之后,这个Buffer被回收复用。IBufferWriter还于用表单的uri编码,编码产生的Buffer不用申请新的内存内容,直接写入表单的HttpContent。
更少的编码操作
WebApiClientCore的json不再使用utf16的string中间类型,直接将对象序列化为网络请求需要的utf8编码二进制json;表单的key和Value编码时,也不产生string中间类型,而是编码后的二进制数据内容,然后写入表单的IBufferWriter。
更快的缓存查找
WebApiClient创建代理类实例来执行一个请求时,要查找两次缓存:通过接口类型查找字典缓存的接口代理类,然后实例化代理类;在ApiInterceptor里面通过MethodInfo查找字典缓存的ApiActionDescriptor。
Refit执行同样逻辑也使用了两次字典缓存,接口和接口代理类安全字典缓存TypeMapping,接口和接口方法描述的字典缓存interfaceHttpMethods。
WebApiClientCore取消了字典缓存,使用静态泛型类的字段作缓存,因为字段访问远比字典查找高效。同时通过巧妙的设计,在代理类拦截方法执行时,直接回传IActionInvoker替换原来的MethodInfo,IActionInvoker包含了ApiActionDescriptor,而IActionInvoker与代理类型都一起缓存在静态泛型类的字段,减少了一次必须的字典缓存查找过程。
性能对比
排除掉真实的网络请求IO等待时间,WebApiClientCore使用的cpu时间仅仅为WebApiClient.JIT和Refit的三分之一。
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.836 (1903/May2019Update/19H1)
Intel Core i3-4150 CPU 3.50GHz (Haswell), 1 CPU, 4 logical and 2 physical cores
.NET Core SDK=3.1.202
[Host] : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
DefaultJob : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
Method | Mean | Error | StdDev |
---|---|---|---|
HttpClient_GetAsync | 3.146 μs | 0.0396 μs | 0.0370 μs |
WebApiClientCore_GetAsync | 12.421 μs | 0.2324 μs | 0.2174 μs |
Refit_GetAsync | 43.241 μs | 0.6713 μs | 0.6279 μs |
Method | Mean | Error | StdDev |
---|---|---|---|
HttpClient_PostJsonAsync | 5.263 μs | 0.0784 μs | 0.0733 μs |
WebApiClientCore_PostJsonAsync | 13.823 μs | 0.1874 μs | 0.1753 μs |
Refit_PostJsonAsync | 45.218 μs | 0.8166 μs | 0.7639 μs |
4 Nuget包与文档
Nuget包
<PackageReference Include="WebApiClientCore" Version="1.0.0" />
项目地址与文档
点击项目链接,带你GET到N种使用技能,不求star,只求提供良好建议。
为.netcore助力--WebApiClient正式发布core版本的更多相关文章
- Docker系列之AspNetCore Runtime VS .NetCore Runtime VS .NET Core SDK(四)
前言 接下来我们就要慢慢步入在.NET Core中使用Docker的殿堂了,在开始之前如题,我们需要搞清楚一些概念,要不然看到官方提供如下一系列镜像,我们会一脸懵逼,不知道到底要使用哪一个. AspN ...
- .NetCore技术研究-.NET Core迁移前的准备工作
前段时间迁移.NET Core做了大量的试水和评估,今天整理一下分享给大家.大致有以下几个部分: 1. .NET Core的由来 2. 为什么要迁移.NET Core 3. .NET Core3.X主 ...
- .net core 版本支持
NetCore sdk并不是每个版本都支持VS2017工具,也不是每个版本的sdk版本号和Runtime版本号都一样,这就需要我们在创建某个版本的net core应用时注意:使用不同版本的vs时需要对 ...
- 如何查看自己电脑支持OpenGL core版本
1. 起因: 红宝书上的例子在电脑上运行后没有效果,但是怎么也找不到原因,反复对看了书上的源码和代码发现没有任何问题,但是就是没有树上写的效果 2. 思路:查看函数的说明,这里推荐使用docs.gl, ...
- .NET Core版本七牛云SDK使用
一.问题背景 公司目前正在将一部分的业务从.NET平台准备迁移到.NET Core上去,同时也准备启用docker进行.NET Core的部署,在项目迁移过程中,不可避免的碰到有些SDK只有在.NET ...
- Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码
Roslyn 是微软为 C# 设计的一套分析器,它具有很强的扩展性.以至于我们只需要编写很少量的代码便能够编译并执行我们的代码. 作为 Roslyn 入门篇文章之一,你将可以通过本文学习如何开始编写一 ...
- .NET Core 版本不支持的问题
分析一个.NET Core开源项目(Ocelot),打开后发现提示当前版本.NET Core 不支持. 错误信息如下: The current .NET SDK does not support ta ...
- linux,发布.netcore填坑,自动升级core版本后,运行报错:'Microsoft.AspNetCore.App', version '3.0.0' was not found.
近来有点空,所以研究下,netcore这个神器~ 号称跨平台的利器,从此net不在局限于windows服务器了,早点该多好呀,这样我们net程序员,不香吗? 网上搜,‘netcore 发布liu ...
- [开源ORM] SqliteSugar 3.x .net Core版本成功上线
SqliteSqlSugar 3.X API 作为支持.NET CORE 为数不多的ORM之一,除了具有优越的性能外,还拥有强大的功能,不只是满足你的增,删,查和改.实质上拥有更多你想像不到的功能,当 ...
随机推荐
- SpringMVC 设置全局DateTime json返回格式
对于部分返回DateTime的项目,只需要在指定属性上添加@JsonSerialize 使用自定义的json转换格式即可自定义返回DateTime格式 但是对于项目中返回有多个DateTime字段来说 ...
- VS2019 使用
下载 官网下载:链接 安装 1.点击下载程序,会显示这个界面 2.点击“继续”,等待安装程序安装完成 4.安装程序下载安装验证完毕,将会提示进入这个界面 5.为了方便起见,这里仅展示安装C++功能,在 ...
- Spring全家桶之springMVC(四)
路径变量PathVariable PathVariable Controller除了可以接收表单提交的数据之外,还可以获取url中携带的变量,即路径变量,此时需要使用@PathVariable ...
- 在终端输入npm run serve时出现npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! test_vue_0613@1.0.0 dev: 错误的解决方法
在vscode终端使用命令 npm run serve 的时候报错 错误原因在于由于文件 node_modules 太大,在项目上传时有些人会删掉 导致我们下载的项目中缺少这个文件 在尝试把自己项目的 ...
- python实现简单投资复利函数以及实现摇骰子猜大小函数
复利函数: #!/user/bin/env python #-*-coding:utf-8 -*- #Author: qinjiaxi def invest(amount, rate, time): ...
- 网鼎杯2020青龙组writeup-web
本文首发于Leon的Blog,如需转载请注明原创地址并联系作者 AreUSerialz 开题即送源码: <?php include("flag.php"); highligh ...
- git:error: Your local changes to the following files would be overwritten by merge:
最近用git在服务器.github.本地更新代码的时候,因为频繁修改偶尔出现这个错误 覆盖本地的代码: git stash git pull git stash pop 保留对服务器上的修改: git ...
- Java基础之值传递
一.传递类型 我们从c语言开始学习程序设计语言时就知道,参数的传递类型一般有两种:值传递和引用传递.那么什么是值传递什么是引用传递呢? 值传递:指在调用方法时将实际参数的值拷贝一份传递给方法,这样方法 ...
- Django之templates模板
模板渲染: 官方文档:https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-for 模板渲染两种 ...
- zz 通过INFORMATION_SCHEMA.INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS 三个表获取事务与锁的信息
zz from http://imysql.com/2015/03/25/mysql-faq-how-to-fetch-latest-trxid.shtml #先查询 INNODB_TRX 表,看看都 ...