工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题

时间:2023-02-11 14:08:06

在 .NET Framework 与 .NET Core 中 HttpClient 有个臭名昭著的问题,HttpClient 实现了 IDispose 接口,但当你 Dispose 它时,它不会立即关闭所使用的 tcp 连接,而是将 tcp 连接置为 TIME_WAIT 状态,240秒(4分钟)后才真正关闭连接。对于高并发的场景,比如每秒 1000 个请求,每个请求都用到 HttpClient ,4分钟内会堆积24万个 tcp 连接,这样的连接爆棚会拖垮服务器。为了避开这个坑,通常采用的变通方法是使用静态的 HttpClient ,但会带来另外一个臭名还没昭著的问题,当 HttpClient 请求的主机名对应的 IP 地址变更时,HttpClient 会蒙在鼓里毫不知情,除非重启应用程序。

为了彻底解决这两个问题,解救广大 .NET 开发人员,HttpClientFactory 在 .NET Core 2.1 中闪亮登场。那 HttpClientFactory 是如何解决问题的呢?让我们一起来参观一下这个有点特别的工厂。

工厂地址在微软市 github 区 aspnet 街 105584022 号 ,https://github.com/aspnet/HttpClientFactory

参观 HttpClientFactory 之前先更多了解一下 HttpClient 的 Dispose 问题。

HttpClient 被 Dispose 时产生 TIME_WAIT 状态的 tcp 连接的本质是在 HttpClient 被 Dispose 时,它所依赖的 HttpMessageHandler 也被 Dispose 了,管理 tcp 连接的正是 HttpMessageHandler ,HttpMessageHandler 是抽象类,落实到实际应用场景通常是 SocketsHttpHandler ,SocketsHttpHandler 通过 HttpConnectionPoolManager 管理着 HttpConnectionPool ,池中养着一堆 HttpConnection 对应的 tcp 连接,Dispose SocketsHttpHandler 影响的通常不是一个 tcp 连接,而是一池 tcp 连接,也就是会将整个池中的所有 tcp 连接都置于 TIME_WAIT 状态,并发量越大,池中的连接越多,Dispose 的杀伤力越大,大到可以会引发 socket exhaustion 。所以,要想解决这个问题就要减少 Dispose 操作,最极端的情况就是使用静态的 HttpClient ,永不 Dispose ,但如前所述这样做的副作用很大。

既要 Dispose HttpClient,又要控制好火候,这是解决这个棘手问题的关键,而 HttpClientFactory 也正是从这个角度出发打造出了一个可定时 Dispose 的工厂。

HttpClientFactory 创建 HttpClient 实例的主要代码如下:

public HttpClient CreateClient(string name)
{
//...
var entry = _activeHandlers.GetOrAdd(name, _entryFactory).Value;
var client = new HttpClient(entry.Handler, disposeHandler: false);
StartHandlerEntryTimer(entry);
//..
return client;
}

为了解决 HttpMessageHandler 的 Dispose 问题,HttpClientFactory 工厂设计制造出了一款新型 HttpMessageHandler —— LifetimeTrackingHttpMessageHandler ,一个有保质期的 HttpMessageHandler (默认是 2 分钟),新生产的 LifetimeTrackingHttpMessageHandler (之后简称 handler)会被放入 _activeHandlers ,过了保质期的 handler 会被放入 _expiredHandlers (有个 Timer 专门在 ExpiryTimer_Tick 回调方法中负责检查保质期), 而在 _expiredHandlers 中的 handler 们会被进一步检查,有个 CleanupTimer 专门在 CleanupTimer_Tick 回调方法中每隔10秒负责检查,进一步检查什么呢?检查这些过期产品(handler)是否可以作废(Dispose),怎么检查的?通过 WeakReference ,代码如下:

internal class ExpiredHandlerTrackingEntry
{
private readonly WeakReference _livenessTracker; public ExpiredHandlerTrackingEntry(ActiveHandlerTrackingEntry other)
{
Name = other.Name; _livenessTracker = new WeakReference(other.Handler);
InnerHandler = other.Handler.InnerHandler;
} public bool CanDispose => !_livenessTracker.IsAlive;
public HttpMessageHandler InnerHandler { get; }
public string Name { get; }
}

如果 _expiredHandlers 中的 handler 已经被 GC 回收(同时也说明对应的 HttpClient 也被 GC 回收),那就 Dispose 掉它。

HttpClientFactory 就是这样通过 2 个定时器有条不紊地控制着 Dispose HttpMessageHandler 释放 TCP 连接的火候,避免在同一时间 Dispose 太多 HttpMessageHandler 引发的 socket exhaustion 解决了 HttpClient 臭名昭著的问题。

工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题的更多相关文章

  1. .NET Core中NETSDK1061错误解决(转载)

    NETSDK1061错误解决 在vs生成和运行都正常,发布的时候报错 .netcore控制台项目引用另一个类库 错误信息 NETSDK1061: 项目是使用 Microsoft.NETCore.App ...

  2. 如何在ASP.NET Core 中使用IHttpClientFactory

    利用IHttpClientFactory可以无缝创建HttpClient实例,避免手动管理它们的生命周期. 当使用ASP.Net Core开发应用程序时,可能经常需要通过HttpClient调用Web ...

  3. 在 .NET Core 中结合 HttpClientFactory 使用 Polly(下篇)

    译者:王亮作者:Polly 团队原文:http://t.cn/EhZ90oq声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的(包括标题).其中可能会去除一些不影响理解但本人实在不知道如 ...

  4. 在 .NET Core 中结合 HttpClientFactory 使用 Polly(中篇)

    译者:王亮作者:Polly 团队原文:http://t.cn/EhZ90oq声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的(包括标题).其中可能会去除一些不影响理解但本人实在不知道如 ...

  5. 在 .NET Core 中结合 HttpClientFactory 使用 Polly(上篇)

    译者:王亮作者:Polly 团队原文:http://t.cn/EhZ90oq 译者序一:前两天写了一篇文章 .NET Core 开源项目 Polly 介绍,在写这篇文章查看 Polly 资料时,看到了 ...

  6. 第十七节:.Net Core中新增HttpClientFactory的前世今生

    一. 背景 1.前世 提到HttpClient,在传统的.Net版本中简直臭名昭著,因为我们安装官方用法 using(var httpClient = new HttpClient()),当然可以Di ...

  7. 你的眼睛背叛你的心:解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题

    在我们将站点从 ASP.NET + Windows 迁移至 ASP.NET Core + Linux 的过程中,目前遇到的最大障碍就是 —— 没有可用的支持 .NET Core 的 memcached ...

  8. 解决 .net core 中 nuget 包版本冲突问题

    今天在一个 asp.net core 项目中遇到了 nuget 包版本冲突的问题,错误信息如下: Version conflict detected for Microsoft.AspNet.WebA ...

  9. 在ASP.NET Core中用HttpClient(六)——ASP.NET Core中使用HttpClientFactory

    ​到目前为止,我们一直直接使用HttpClient.在每个服务中,我们都创建了一个HttpClient实例和所有必需的配置.这会导致了重复代码.在这篇文章中,我们将学习如何通过使用HttpClient ...

随机推荐

  1. A.Kaw矩阵代数初步学习笔记 10. Eigenvalues and Eigenvectors

    “矩阵代数初步”(Introduction to MATRIX ALGEBRA)课程由Prof. A.K.Kaw(University of South Florida)设计并讲授. PDF格式学习笔 ...

  2. netbean使用技巧

    1.让代码智能提示 有些情况下Ctrl+Space这个键被一些输入法占了,我们需要修改一下点击 工具->常规->快捷键映射->找到显示代码完成弹出式菜单->编辑为你喜欢的键就好 ...

  3. android doc 本地文档加载慢的解决办法

    从来都是FQ上谷歌官网查文档,但是有时没办法FQ,就得用sdk本地的doc文档了,由于文档内部的一些javascript,font等也需要访问Google来加载,导致了打开本地网页也巨慢无比,甚至转了 ...

  4. 报错:Failed to create BuildConfig class

    1. Project | Clean| Build Automatically 2. 在目录中找到gen目录,手动删除(一般是没有写权限导致Failed),再执行第一步方案.

  5. linux devel包 和 非devel包的区别

    devel 包主要是供开发用,至少包括以下2个东西: 1. 头文件 2. 链接库 有的还含有开发文档或演示代码. 以 glib 和 glib-devel 为例: 如果你安装基于 glib 开发的程序, ...

  6. win8.1 64位+oracle11g R2 64位 +powerdesigner破解版 64位+PL/SQL

    安装时搜索了很多帖子,很多就是复制粘贴(完全不需要什么IP,host),有的版本不对,有的版本太老,今天决定贴出自己的处女贴 oracle的安装很简单,不需要说什么了,PL/SQL真是恶心死 orac ...

  7. 【CSA49F】【XSY3317】card 博弈论 DP

    题目大意 不会博弈论的 yww 在和博弈论大师 yxq 玩一个游戏. 有 \(n\) 种卡牌,第 \(i\) 种卡牌有 \(b_i\) 张. yww 会先把所有 \(B=\sum_{i=1}^nb_i ...

  8. bzoj3678 Katu Puzzle

    题目链接 题意 给定一张图,对于每条边给出一个运算符\((\&,|,\otimes)\)和一个值\(c(0 \le c \le 1)\).问能否通过给每个点赋上一个值.使得每条边通过指定的运算 ...

  9. Chapter 4 Invitations——18

    But they were all in, and Edward was speeding away. 但是他们都在里面了之后,Edward就加速走了. I drove home slowly, ca ...

  10. Centos6与Centos7安装和使用htop

    Centos6 安装 rpm -ivh https://mirrors.tuna.tsinghua.edu.cn/epel/6/x86_64/epel-release-6-8.noarch.rpm r ...