数据结构论坛

首页 » 分类 » 定义 » HttpClientFactory的套路
TUhjnbcbe - 2023/12/22 20:22:00

背景

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

1
查看完整版本: HttpClientFactory的套路