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,只求提供良好建議。