.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中引入的,应用在启动时构建主机,主机作为一个对象用于封装应用资源以及应用程序启动和生存期管理.其主要功能包括配置初始化(包括加载配置以 ...
随机推荐
- DLL中类的显式链接(用虚函数进行显式链接)
DLL的显式链接在某些时候比隐式链接具有更大的灵活性.比如,如果在运行时发现DLL无法找到,程序可以显示一个错误信息并能继续运行.当你想为你的程序提供插件服务时,显式链接也很有用处. 显式链接到全局C ...
- qlineedit设置背景颜色(使用QPalette的方法不行,必须使用QSS)
使用QPalette的方法不行, ui->le_text->setAutoFillBackground(true);qDebug() << ui->le_text-> ...
- 文件文件夹混合多选对话框(修改GWL_WNDPROC)
/******************************************************************** created: 2008/07/22 created: 2 ...
- SPOJ1421_Goods_循环节
题意:1-n的一个排列,两两互换,每个位置每天只能做一次交换,问最多几天能交换成1-n,并且输出交换步骤. 解法:把该置换中所有的循环节找出,各循环节之间的交换是并行的,两两不相关,每天只需在循环节内 ...
- WIN8安装oracle11g时出现不满足最低配置解决办法
Windows8上面安装Oracle11g客户端和服务端时都会出现这样的错误提示:[INS-13001]环境不满足最低要求 产生这种报错的主要原因在于:oracle 11g的配置文件中并没有提供匹配w ...
- Quartz.Net实现作业定时调度详解
1.Quartz.NET介绍 Quartz.NET是一个强大.开源.轻量的作业调度框架,你能够用它来为执行一个作业而创建简单的或复杂的作业调度.它有很多特征,如:数据库支持,集群,插件,支持cron- ...
- Django的的安装和配置
1. 下载 1. 命令行 pip install django==1.11.18 -i https://pypi.douban.com/simple/ 2. 创建项目 1. 命令行 django-ad ...
- never下sqlcient
[一]参数的输入 如执行update,我们写的代码应该是 sqlclient.Update(,, },@Name = "eee" }): 表示更新Id =1,2,3这三行的信息.这 ...
- Python自学day-1
一.Python介绍 1.python擅长领域: WEB开发:Django. pyramid. Tornado. Bottle. Flask. WebPy 网络编程:Twisted(牛 ...
- spring 5.x 系列第3篇 —— spring AOP (xml配置方式)
文章目录 一.说明 1.1 项目结构说明 1.2 依赖说明 二.spring aop 2.1 创建待切入接口及其实现类 2.2 创建自定义切面类 2.3 配置切面 2.4 测试切面 附: 关于切面表达 ...