.NET Core 3.0之深入源码理解HttpClientFactory(一)
写在前面
创建HttpClient实例的时候,在内部会创建HttpMessageHandler链,我们知道HttpMessageHandler是负责建立连接的抽象处理程序,所以HttpClient的维护实际上就是维护HttpMessageHandler的使用,释放HttpClient并不会及时释放连接,而通常情况下一般是创建全局使用的HttpClient实例,以减少重复连接的次数。当然这种方式所带来的的弊端也是显而易见的,因为当前的HttpClient实例所指向的服务器发生问题或者DNS发生变更,那么该实例是无法做到自动更新指向的。
以下为运行其流程图:

HttpClientFactory自.NET Core 2.1引入,可以认为它是一个配置和创建HttpClient的中心化,.NET Core通过引入HttpClientFactory用于自动化维护HttpMessageHandler池及其生命周期,避免在手动管理 HttpClient生存期时出现的常见 DNS 问题。在默认情况下MessageHandler的活跃状态是两分钟,也就是说,在两分钟后,就可以为HttpClient实例重新定位到正确的主机上。
本文的讨论思路将从我们能看到的代码开始一步步深入。
详细介绍
HttpClientFactory的功能主要位于Microsoft.Extensions.Http包中,它已经默认包含在Microsoft.AspNetCore.App元包中。针对HttpClientFactory的处理涉及到IHttpClientBuilder、IHttpClientFactory、IHttpMessageHandlerFactory、ITypedHttpClientFactory这几大接口,以下将分别做讨论。
services.AddHttpClient()
我们在创建或者配置HttpClient对象的时候,会在ConfigureServices方法中增加services.AddHttpClient(),即可注册IHttpClientFactory。
这段代码位于Microsoft.Extensions.DependencyInjection.HttpClientFactoryServiceCollectionExtensions中,它会初始化相关信息并注册到IServiceCollection中,这些信息包括日志、选项、核心抽象功能、类型客户端以及其他基础设施功能。
需要注意的是,在核心抽象功能中,DefaultHttpClientFactory是单例模式的,其所继承的接口对象的获取也是单例的,而HttpMessageHandlerBuilder注册方式确是每一次GetService的时候都会创建一个新的HttpMessageHandlerBuilder实例。
以下为services.AddHttpClient()的源代码,其中标红部分为核心抽象功能的注册:
1: public static IServiceCollection AddHttpClient(this IServiceCollection services)
2: {
3: if (services == null)
4: {
5: throw new ArgumentNullException(nameof(services));
6: }
7:
8: services.AddLogging();
9: services.AddOptions();
10:
11: services.TryAddTransient<HttpMessageHandlerBuilder, DefaultHttpMessageHandlerBuilder>(); 12: services.AddSingleton<DefaultHttpClientFactory>(); 13: services.TryAddSingleton<IHttpClientFactory>(serviceProvider => serviceProvider.GetRequiredService<DefaultHttpClientFactory>()); 14: services.TryAddSingleton<IHttpMessageHandlerFactory>(serviceProvider => serviceProvider.GetRequiredService<DefaultHttpClientFactory>());
15:
16: services.TryAdd(ServiceDescriptor.Transient(typeof(ITypedHttpClientFactory<>), typeof(DefaultTypedHttpClientFactory<>)));
17: services.TryAdd(ServiceDescriptor.Transient(typeof(DefaultTypedHttpClientFactory<>.Cache), typeof(DefaultTypedHttpClientFactory<>.Cache)));
18:
19: services.TryAddEnumerable(ServiceDescriptor.Singleton<IHttpMessageHandlerBuilderFilter, LoggingHttpMessageHandlerBuilderFilter>());
20:
21: return services;
22: }
DefaultHttpClientFactory
DefaultHttpClientFactory是一个用internal修饰的类,意味着该类只能在在其内部使用。它继承了IHttpClientFactory、IHttpMessageHandlerFactory这两个接口。由此可见,DefaultHttpClientFactory实例的创建被拆成了两种行为。
IHttpClientFactory的定位是一个抽象工厂,可以为指定名称的HttpClient实例创建自定义配置,它只有一个方法,HttpClient CreateClient(string name)。
IHttpMessageHandlerFactory的定位也是一个抽象工厂,它为指定名称的HttpMessageHandler实例创建自定义配置,它只有一个方法,HttpMessageHandler CreateHandler(string name)。
我们先看一下这两个方法的实现,会觉得很有意思
1: public HttpClient CreateClient(string name)
2: {
3: if (name == null)
4: {
5: throw new ArgumentNullException(nameof(name));
6: }
7:
8: var handler = CreateHandler(name);
9: var client = new HttpClient(handler, disposeHandler: false);
10:
11: var options = _optionsMonitor.Get(name);
12: for (var i = 0; i < options.HttpClientActions.Count; i++)
13: {
14: options.HttpClientActions[i](client);
15: }
16:
17: return client;
18: }
19:
20: public HttpMessageHandler CreateHandler(string name)
21: {
22: if (name == null)
23: {
24: throw new ArgumentNullException(nameof(name));
25: }
26:
27: var entry = _activeHandlers.GetOrAdd(name, _entryFactory).Value;
28:
29: StartHandlerEntryTimer(entry);
30:
31: return entry.Handler;
32: }
可以看到,我们通过名称查找HttpClient对象的时候,也会依照该名称以GetOrAdd方式去查找相应的HttpMessageHandler对象,也就说HttpClient对象和HttpMessageHandler对象可以通过名称关联起来。
需要注意的时候在调用CreateHandler方法的时候会调用StartHandlerEntryTimer方法,这个方法是干嘛的呢,他维护着定时器。该方法位于Microsoft.Extensions.Http.ActiveHandlerTrackingEntry中,我们将此类视为是一个不可变的(当然,其内部的定时器是变化的),为“到期”池创建一个可以显著简化线程需求的新对象。
除了这两个方法外,我们要需要注意DefaultHttpClientFactory对HttpMessageHandler的管理功能。DefaultHttpClientFactory内部维护者一个定时器和两个HttpMessageHandler对象集合,这两个集合分别是ActiveHandler和ExpiredHandler。内部定时器会定期从ExpiredHandler集合中扫描并清理无效的 HttpMessageHandler对象。
ActiveHandler集合的增加是在调用CreateHandler方法时增加的,其移除是在回调的时候移除,这个移除入口也只有这一处。
ExpiredHandler集合的增加也是在调用CreateHandler方法时,通过内部的一个回调机制增加的,其移除通过定时器定期扫描来实现的。这处需要注意的是,ExpiredHandlerTrackingEntry这个类中有一个属性,代码如下:
1: private readonly WeakReference _livenessTracker;
1: public bool CanDispose => !_livenessTracker.IsAlive;
通过WeakReference 类型的变量来标识该HttpMessageHandler对象是否应该被从集合中移除。
定时器一般是个比较消耗资源,而且一旦用不好,就会引发线程问题,DefaultHttpClientFactory在处理定时器的时候,首先通过停止所有挂起的计时器,在清除后如果还需要继续处理无效HttpMessageHandler对象,将会重新启动计时器,虽然看似多余了点,但是比通过锁定整个清理机制来确定是否阻塞清理任何并启动定时器要好多了。
1: internal void CleanupTimer_Tick()
2: {
3: StopCleanupTimer();
4:
5: if (!Monitor.TryEnter(_cleanupActiveLock))
6: {
7: StartCleanupTimer();
8: return;
9: }
10:
11: try
12: {
13: var initialCount = _expiredHandlers.Count;
14: Log.CleanupCycleStart(_logger, initialCount);
17:
18: var disposedCount = 0;
19: //开始清理
20:
21: Log.CleanupCycleEnd(_logger, stopwatch.GetElapsedTime(), disposedCount, _expiredHandlers.Count);
22: }
23: finally
24: {
25: Monitor.Exit(_cleanupActiveLock);
26: }
27:
28: if (_expiredHandlers.Count > 0)
29: {
30: StartCleanupTimer();
31: }
32: }
以下为这两个队列的处理示意图:

.NET Core 3.0之深入源码理解HttpClientFactory(一)的更多相关文章
- .NET Core 3.0之深入源码理解HttpClientFactory(二)
写在前面 上一篇文章讨论了通过在ConfigureServices中调用services.AddHttpClient()方法,并基于此进一步探讨了DefaultHttpClientFactory是 ...
- .NET Core 3.0之深入源码理解Startup的注册及运行
原文:.NET Core 3.0之深入源码理解Startup的注册及运行 写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程 ...
- .NET Core 3.0之深入源码理解Configuration(一)
Configuration总体介绍 微软在.NET Core里设计出了全新的配置体系,并以非常灵活.可扩展的方式实现.从其源码来看,其运行机制大致是,根据其Source,创建一个Builder实例,并 ...
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(一)
写在前面 ASP.NET Core 的 Web 服务器默认采用Kestrel,这是一个基于libuv(一个跨平台的基于Node.js异步I/O库)的跨平台.轻量级的Web服务器. 在开始之前,先回 ...
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(二)
前言 前一篇文章主要介绍了.NET Core继承Kestrel的目的.运行方式以及相关的使用,接下来将进一步从源码角度探讨.NET Core 3.0中关于Kestrel的其他内容,该部分内容,我们 ...
- .NET Core 3.0之深入源码理解Host(二)
写在前面 停了近一个月的技术博客,随着正式脱离996的魔窟,接下来也正式恢复了.本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点,主要讨论Long Run Pro ...
- .NET Core 3.0之深入源码理解ObjectPool(一)
写在前面 对象池是一种比较常用的提高系统性能的软件设计模式,它维护了一系列相关对象列表的容器对象,这些对象可以随时重复使用,对象池节省了频繁创建对象的开销. 它使用取用/归还的操作模式,并重复执行这些 ...
- .NET Core 3.0之深入源码理解HealthCheck(一)
写在前面 我们的系统可能因为正在部署.服务异常终止或者其他问题导致系统处于非健康状态,这个时候我们需要知道系统的健康状况,而健康检查可以帮助我们快速确定系统是否处于正常状态.一般情况下,我们会提供公开 ...
- .NET Core 3.0之深入源码理解Host(一)
写在前面 ASP .NET Core中的通用主机构建器是在v2.1中引入的,应用在启动时构建主机,主机作为一个对象用于封装应用资源以及应用程序启动和生存期管理.其主要功能包括配置初始化(包括加载配置以 ...
随机推荐
- 我写的一个Qt 显示二维码( QR Code)的控件(可以去掉对 libpthread 的依赖,而且编译出的库文件可以在 vc2010 的release 模式下使用)
最近一个项目需要显示二维码,所以花了点时间(只用了一个晚上,写的很不完善),写了个显示二维码的控件.当然这个控件用到了些开源的代码,比如qrencode,所以我也打算把我的代码开源. 我的代码参考了 ...
- Qt4.85静态编译配置VS动态编译(非常详细的图文教程)
http://www.qter.org/forum.php?mod=viewthread&tid=1409&extra=page%3D1&page=1
- 只言片语 - cell 图片复用问题
一. 今日做项目遇到图片复用问题,返回cell高度相同,由于网络不好出现图片复用,发现问题 Cell 图片加载方法如下: - (void)sd_setImageWithURL:(NSURL *)u ...
- Silverlight ItemsControl详细解析+解惑
Silverlight最强大的地方就在于定义控件了,Silverlight提供了非常灵活和高效的控件定义方式,几乎可以实现任何复杂的控件实现,对于快速开发应用程序有着重要的意义.在Silverligh ...
- java之jdbc学习——QueryRunner
jdbc是ORM框架的基础,但将数据库中的表映射到java对象,并进行增删改查,并不是一件简单的事情. 涉及到jdbc.注解和反射的一些基础知识. 以下内容来自网友的分享,并做了一些增减,作为笔记记录 ...
- java多线程之wait和notify
多线程中的通信是非常重要的概念,线程直接实现通信就可以并发完成很多复杂工作. java在Object类中就设计了wait()和notify()两个方法,以解决这个问题. 1.释义: wait()方法将 ...
- Python的魔术方法详解
构造和初始化 __init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数". 实际上, 当我们调用x = SomeClass()的时候调用,__init_ ...
- Jenkins的安装与使用(一)
jenkins是一个广泛用于持续构建的可视化web工具,持续构建说得更直白点,就是各种项目的"自动化"编译.打包.分发部署.jenkins可以很好的支持各种语言(比如:java, ...
- 如何做一个大数据seo人员
作为流量运营者或者SEO人员,对于所从事行业领域的认识往往建立在一种直觉之上,我们很难对一个行业有一个全面的了解,这个行业领域有多宽,流量聚焦在哪里,那些是用户最关心的问题? 有的时候很难准确的把握, ...
- SHELL 中条件语句的运用 if for 条件测试语句
if条件测试语句可以让脚本根据实际情况自动执行相应的命令.从技术角度来讲,if语句分为单分支结构.双分支结构.多分支结构:其复杂度随着灵活度一起逐级上升. if条件语句的单分支结构由if.then.f ...