WebApiClient已成熟稳定,发布了WebApiClient.JIT和WebApiClient.AOT两个nuget包,累计近10w次下载。我对它的高可扩展性设计至关满意和自豪,但WebApiClient并不所以而停下脚步,在一年前,我产生了编写其core版本的想法,将asp.netcore服务端先进的思想融入到core版本,在性能与扩展性上获得进一步升华。
对应的,给它叫了WebApiClientCore的名字,为了对得起名字里面的Core字,我在框架设计、性能优化上占用总体开发时间一半以上。git
WebApiClient时尚未IActionInvoker概念,对应的执行逻辑直接在ApiActionContext上实现。如今我以为,Context应该是一个状态数据类,而不能也成为一个执行者,由于一个执行者的实例能够无限次地执行多个Context实例。github
Refit则更简单粗暴,将全部实现都在一个RequestBuilderImplementation的类上:大家只要也只能使用我内置的Attribute声明,一切执行在我这个类里面包办,由于我是一个万能类。json
Core版本增长了IActionInvoker概念,从中Context分开,用于执行Context,职责分明。在实现上又分为多种Invoker:Task声明返回执行者ActionInvoker、ITask声明返回处理处理者ActionTask,以及聚合的MultiplexedActionInvoker。api
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; }; }
WebApiClient只有一个ApiActionContext,其Result和Exception属性在请求前就能够访问或设置,但实际上就算设置了值,流程也不会短路和中断,属于设计失误。restful
Refit没有相关Context概念,由于它不提供给用户自定义扩展Attribute的能力,它内置的Attribute也没有执行能力,一个RequestBuilderImplementation类够了。网络
Core版本将设计了多个Context概念,不一样阶段有不一样的Context,如同asp.netcore不一样Filter的Context也不一样同样。对于一个Action,请求阶段对应是ApiRequestContext,响应阶段是ApiResponseContext;对于Action的参数,对应是ApiParameterContext。每种Context里面都包含核心的HttpContext属性,HttpContext包含请求消息、响应消息和接口配置选项等。
输入WebApiClientCore命名空间,会发现定义了不少Interface,这些Interface都是为了用户实现自定义特性用的,固然内置的全部特性,都是实现了这些接口而已。若是一个特性实现了多个接口,它就有多种能力,好比内置的HeaderAttribute,它既能够修饰于Interface和Method,也能够修饰参数。
WebApiClientCore的Attribute描述的逻辑,是由Attribute自我实现,因此整个请求的数据装配逻辑是分散为各个Attribute上,用什么Attribute就有什么逻辑,包含框架以外的非内置的自定义Attribute。
Refit的内置Attribute只有欲描述逻辑,没有实现逻辑,实现逻辑由RequestBuilderImplementation包办,因此它不须要接口也没有接口。
像[HttpGet("objects/{id}")]这样的path参数,在restful中经常遇到,经过Span优化,Core版本在替换path参数值cpu占用时间下降为原版本的十分之一。
得益于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 |
<PackageReference Include="WebApiClientCore" Version="1.0.0" />
点击项目连接,带你GET到N种使用技能,不求star,只求提供良好建议。