为.netcore助力--WebApiClient正式发布core版本
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?聲明返回執行者?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.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?替換原來的?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 RyuJITDefaultJob : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT| 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 |
| 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版本的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core使用Nacos
- 下一篇: 一个static和面试官扯了一个小时,舌