1、前言

NCC WebApiClient 已成熟稳定,发布了WebApiClient.JIT 和 WebApiClient.AOT 两个 NuGet 包,累计近 10w 次下载。

我对它的高可扩展性设计相当满意和自豪,但 WebApiClient 并不因此而停下脚步,在一年前,我产生了编写其 Core 版本的想法,将 ASP.NET Core 服务端先进的思想融入到 Core 版本,在性能与扩展性上得到进一步升华。

对应的,给它叫了 WebApiClientCore 的名字,为了对得起名字里面的 Core 字,我在框架设计、性能优化上占用整体开发时间一半以上。

2、框架设计

IActionInvoker

WebApiClient 时还没有 IActionInvoker 概念,对应的执行逻辑直接在 ApiActionContext 上实现。现在我觉得,Context 应该是一个状态数据类,而不能也成为一个执行者,因为一个执行者的实例可以无限次地执行多个 Context 实例。

Refit 则更简单粗暴,将所有实现都在一个 RequestBuilderImplementation 的类上:你们只要也只能使用我内置的 Attribute 声明,一切执行在我这个类里面包办,因为我是一个万能类。

Core 版本增加了 IActionInvoker 概念,从中 Context 分开,用于执行 Context,职责分明。在实现上又分为多种 Invoker:Task 声明返回执行者 ActionInvokerITask 声明返回处理处理者 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.NET Core 不同 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 替换原来的 MethodInfoIActionInvoker 包含了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 RyuJITDefaultJob : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
Method Mean Error Std
Dev
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 Std
Dev
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,只求提供良好建议。

https://github.com/dotnetcore/WebApiClient

为.netcore助力--WebApiClient正式发布core版本相关推荐

  1. .NET Core版本七牛云SDK使用

    一.问题背景 公司目前正在将一部分的业务从.NET平台准备迁移到.NET Core上去,同时也准备启用docker进行.NET Core的部署,在项目迁移过程中,不可避免的碰到有些SDK只有在.NET ...

  2. 任务队列和异步接口的正确打开方式(.NET Core版本)

    layout: post title: 任务队列和异步接口的正确打开方式(.NET Core版本) category: dotnet core date: 2019-01-12 tags: dotne ...

  3. [SSCore] 开源dotnet core 版本 SuperSocket

    前言碎语 最近一直在做旧版本dotnet 程序迁移至dotnet core的工作, 非常欣慰dotnet社区的蓬勃发展, 目前大部分的第三方类库或开源代码都有了dotnet core版本 或者可以方便 ...

  4. 阿里云服务网格 ASM 正式发布商业化版本

    简介:为了更好地满足企业日益加深的大规模使用服务网格产品.服务多语言互通.服务精细治理等需求,2022 年 4 月 1 日起,阿里云服务网格产品 ASM 正式发布商业化版本,为企业在生产环境下大规模落 ...

  5. asp.netcore oracle,Asp.net core 3.1+EF Core2.2.6+Oracle.EntityFrameworkCore2.1.19连接Oracle数据库...

    Asp.net Core 3.1+EF Core2.2.6+Oracle.EntityFrameworkCore2.1.19连接Oracle数据库 1.前言 本次主要采用Asp.net core3.1 ...

  6. 【分享】154页微软WPF官方手册(含.NETCore和.NET Framwork双版本)

    物联网IOT多场景概念落地,带火了WPF招聘,像阿里影视.百度地图.小米小鹏特斯拉都在高薪抢WPF人才了.机智的.NET开发者去关注学习WPF的时候却发现,市面上真的太缺WPF优秀的教程了,还好这里有 ...

  7. 正式发布python版本的年份_飞书全新版本π正式发布 高效便捷的团队沟通新工具...

    11月18日,飞书未来无限大会正式开幕,作为字节跳动旗下办公协作平台,飞书的第一场产品发布会,不仅有诸多新功能首度亮相,而且众多重磅行业客户也共聚一堂,多元视角探讨飞书新生态.飞书总裁张楠表示,飞书既 ...

  8. .NET Core版本揭秘

    目录 背景 使用代码 兴趣点 在本技巧中,您将学习一种简单的方法来为.NET Core 2.0项目提供通用的版本号和版本信息. 背景 我们有一个VS2017解决方案,其中包含数百个项目,大部分使用C# ...

  9. 区块链浏览器_YOYOW正式发布更新版本区块链浏览器

    (点击↑↑图片领"福利") YOYOW主网2.0顺利上线以来,YOYOW基金会团队与合作伙伴持续开展合作,多家合作伙伴成为并参与到YOYOW主网2.0超级节点的竞选.同时除了主网以 ...

最新文章

  1. 导出excel表格,前端和后台导出
  2. java datainputstream_Java DataInputStream readUnsignedByte()方法
  3. DjangoAdmin站点调整列表页展示
  4. python的调试器_玩转Python调试器
  5. sRGB 和 Adobe RGB 有什么区别?
  6. 一个类中可以没有main方法_一个月可以暴瘦二十斤的减肥方法
  7. va_g729a编码库使用
  8. 微信开发之小程序UI设计规范
  9. 固态硬盘系统经常假死_win10系统更换固态硬盘经常假死的解决方法
  10. 百度编辑器ueditor-在线图片管理,想修改下默认的排序管理
  11. mysql创建表s c sc_MySqL | 小白创建表
  12. 海量文件、超大文件,如何实现高速传输?
  13. java文件读写之Channel策略
  14. 在一个字符串中找出元音字母a,e,i,o,u出现的次数
  15. 使用excel公式vlookup提取多个表中的数据
  16. swift项目调用OC代码,OC项目调用swift代码
  17. (小白都能听懂)的海明校验码
  18. matlab中的电子器件,对电力电子器件控制设计进行硬件在环测试
  19. js数据类型-数字型
  20. c语言文学研究助手报告,文学研究助手数据结构报告.doc

热门文章

  1. linux最常用的20条命令
  2. ubuntu make menuconfig error
  3. mysql 三主_MySQL主主复制3
  4. Teams的MessageExtension最新功能:Initiate actions
  5. 本地服务器下的局域网安全吗_本地安全认证服务器
  6. android 设备占用_如何查看正在占用Android设备的空间
  7. git工具 将源码clone到本地指定目录的三种方式
  8. 修改GIT的user.name和user.email
  9. Devuan Jessie beta 释出
  10. 自从装了windows神器,再也不用羡慕mac了