背景
ASP.NETCore在2.1之后推出了具有弹性HTTP请求能力的HttpClient工厂类HttpClientFactory。
替换的初衷还是简单摆一下:①using(varclient=newHttpClient())调用的Dispose()方法并不会立即释放底层Socket连接,新建Socket需要时间,导致在高并发场景下Socket耗尽。②基于①很多人会想到使用单例或者静态类构造HttpClient实例,但是这里有一个坑,HttpClient不会反应DNS的变更。
HttpClientFactory以模块化、可命名、可配置、弹性方式重建了HttpClient的使用方式:由DI框架注入IHttpClientFactory工厂;由工厂创建HttpClient并从内部的Handler池分配请求Handler。
HttpClient可在DI框架中通过IHttpCLientBuilder对象配置Policy策略。
我一直对这种颠覆传统HttpClient的代码组织方式感到好奇,今天我们带着问题来探究一下新版HttpClient的实现。
与码无瓜
一个完整的HttpClient包括三部分:
基础业务配置:BaseAddress、DefaultRequestHeaders、DefaultProxy、TimeOut……核心MessageHandler:负责核心的业务请求[可选的]附加HttpMessageHandler附加的HttpMessageHandler需要与核心HttpMessageHandler形成链式Pipeline关系,最终端点指向核心HttpMessageHandler,链表数据结构是DelegatingHandler关键类(包含InnerHandler链表节点指针)
刨瓜问底
很明显,HttpClientFactory源码的解读分为2部分,心里藏着伪代码,带着问题思考更香(手动狗头)。
P1.构建HttpClient
在Startup.cs文件开始配置要用到的HttpClient
配置过程充分体现了.NETCore推崇的万物皆服务,配置前移的DI风格;同对时HttpClient的基础、配置均通过配置即委托来完成
Q1.如何记录以上配置?
微软使用一个HttpClientFactoryOptions对象来记录HttpClient配置,这个套路是不是很熟悉?
通过DI框架的AddHttpClient扩展方法产生HttpClientBuilder对象HttpClientBuilder对象的ConfigurePrimaryHttpMessageHandler扩展方法会将核心Handler插到Options对象的HttpMessageHandlerBuilderActions数组,作为Handlers数组中的PrimaryHandlerHttpClientBuilder对象的AddPolicyHandler扩展方法也会将PolicyHttpMessageHandler插到Options对象的HttpMessageHandlerBuilderActions数组,但是作为AdditionHandler
显而易见,后期创建HttpClient实例时会通过name找到对应的Options,从中加载配置和Handlers。
P2.初始化HttpClient实例
通过IHttpClientFactory.CreateClient()产生的HttpClient实例有一些内部行为:标准的HttpClient(不带Policy策略)除了PrimaryHandler之外,微软给你附加了两个AdditionHandler:
LoggingScopeHttpMessageHandler:最外围Logical日志LoggingHttpMessageHandler:核心Http请求日志之后将排序后的AdditionHanders数组与PrimaryHandler通过DelegatingHandler数据结构转化为链表,末节点是PrimaryHandler
输出的日志如下:
Q2.微软为啥要增加外围日志Handler?
这要结合P1给出的带Policy策略的HttpClient,带Policy策略的HttpClient会在AdditionHandlers插入PolicyHttpMessageHandler来控制retry、CircuitBreaker,那么就会构建这样的HandlerPipeline:
所以微软会在AdditionHandlers数组最外围提供一个业务含义的日志LogicalHandler,最内层固定LoggingHttpHandler,这是不是很靠谱?
无图无真相,请查看带Policy策略的HttpClient请求堆栈:
Q3.何处强插、强行固定这两个日志Handler?微软通过在DI环节注入默认的LoggingHttpMessageHandlerBuilderFilter来重排Handler的位置:
Q4.创建HttpClient时,如何将AdditionHandlers和PrimaryHandler形成链式Pipeline关系?
数组转链表IReadOnlyListDelegatingHandler的算法与ASP.NETCore框架的Middleware构建Pipeline如出一辙。
总结
伪代码表示实例创建过程:DefaultHttpClientFactory.CreateClient()
---构造函数由DI注入默认的LoggingHttpMessageHandlerBuilderFilter
---通过Options.HttpMessageHandlerBuilderActions拿到所有的Handler
---使用LoggingHttpMessageHandlerBuilderFilter强排AdditionHandlers
---创建Handler链式管道
---用以上链式初始化HttpClient实例
---从Options.HttpClientActions中提取对于Httpclient的基础配置
---返回一个基础、HttpHandler均正确配置的HttpClient实例
上述行为依赖于ASP.NETCor框架在DI阶段注入的几个服务:
DefaultHttpClientFactoryLoggingHttpMessageHandlerBuilderFilter:过滤并强排AdditionHandlerDefaultHttpMessageHandlerBuilder:Handler数组转链表我们探究System.Net.Http库的目的:
学习精良的设计模式、理解默认的DI行为;
默认DI行为给我们提供了扩展/改造HttpClientFactory的一个思路。
附赠有关应用HttpClient的三个瓜:
1.AllowAutoRedirect属性:控制请求收到重定向响应能发起跳转请求,默认为true
2.AutomaticDe