HttpClient参观记:.net core 2.2 对HttpClient到底做了什么?

.net core 于 10月17日发布了 ASP.NET Core 2.2.0 -preview3,在这个版本中,我看到了一个很让我惊喜的新特性:HTTP Client Performance Improvements ,而且在Linux上性能提升了60% !

之前就一直苦于 HttpClient 的糟糕特性,大家耳熟能详的 You are using HttpClient wrong。
因为 HttpClient 实现了 IDisposable 如果用完就释放,Tcp 连接也会被断开,并且一个HttpClient 通常会建立很多个 Tcp 连接 。 Tcp 连接断开的过程是有一个 Time_Wait 状态的,因为要保证 Tcp 连接能够断开,以及防止断开过程中还有数据包在传送。这本身没有毛病,但是如果你在使用 HttpClient 后就将其注销,并且同时处于高并发的情况下,那么你的 Time_Wait 状态的 Tcp 连接就会爆炸的增长,
他们占用端口和资源而且还迟迟不消失,就像是在 嘲讽 你。所以临时解决方式是使用静态的 HttpClient 对象,No Dispose No Time_Wait

后来在 .net core2.1 中,引入了 HttpClientFactory 来解决这一问题。 HttpClientFactory 直接负责给 HttpClient 输入 全新的 HttpMessageHandle 对象,并且管理 HttpMessageHandle 的生杀大权,这样断开 Tcp 连接的操作都由 HttpClientFactory 来用一种良好的机制去解决。

上面说了一堆,其实和主题关系不大。 因为我在实际生产环境中,无论使用静态的 HttpClient 还是使用 HttpClientFactory ,在高并发下的情况下 Tcp 连接都陡然上升。直到我将 .net core 2.1 升级到 .net core 2.2 preview 问题似乎奇迹般的解决了。在介绍 .net core 2.2 如何提升 HttpClient 性能的时候,需要先简单介绍下 HttpClient :

上面说到了 HttpMessageHandle ( 顾名思义:Http消息处理器 ) 它是一个抽象类,用来干嘛的呢? 处理请求,又是顾名思义。 HttpClient 的发送请求函数 :SendAsync()

   public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption,CancellationToken cancellationToken){....}

最后调用的就是 HttpMessageHandle 的 SendAsync 抽象函数。

事实上通过阅读源码发现,几乎所有继承 HttpMessageHandle 的子类都有一个 HttpMessageHandle 类型的属性 : _handle,而每个子类的 SendAsync 函数都调用 _handle 的 SendAsync()。我们知道在初始化一个 HttpClient 的时候或者使用 HttpClientFactory 创建一个HttpClient 的时候都需要新建 或者传入一个 HttpMessageHandle 我把它叫做起始消息处理器。 很容易想像,HttpClient 的 SendAsync 函数是 一个 HttpMessageHandle 调用 下一个 HttpMessageHanlde 的SendAsync,而下一个 HttpMessageHandle 的SendAsync 是调用下下一个HttpMessageHandle 的 SendAsync 函数。每一个HttpMessageHandle 都有其自己的职责。
层层嵌套,环环相扣,循环往复,生生不息,额不对,这样下去会死循环。 直到它到达终点,也就是Tcp 连接建立,抛弃回收,发送请求的地方。 所以 HttpClient 的核心 就是由这些 HttpMessageHandle 扣起来,打造成一个 消息通道。 每个请求都无一例外的 通过这个通道,找到它们的最终归宿。

这其中的顺序到底是啥,我并不关心,我只关心其中一个 环:SocketsHttpHandle 因为.net core 2.2 就是从这个环开始动了手术刀,怎么动的,按照上面的说法,我们从 SocketHttpHandle 开始顺藤摸瓜。其实顾名思义 SocketsHttpHandle 已经很接近 HttpClient 的通道的末尾了。这是 摸出来的 链条 :

SocketsHttpHandle ----> HttpConnectionHandler/HttpAuthenticatedConnectionHandler ----> HttpConnectionPoolManager ----> HttpConnectionPoolManager

---> HttpConnectionPool

最后一个加粗是有原因的,因为我们摸到尾巴了,HttpConnectionPool( 顾名思义 Http 连接 池) 已经不继承 HttpMessageHandle 了 ,它就是我们要找的终极,也是请求最终获取连接的地方,也是.net core 2.2 在这条链中的 操刀的地方。

接下来就要隆重介绍 手术过程。手术的位置在哪里? 就是获取 Tcp 连接的函数。我们看手术前的样子,也就是System.Net.Http 4.3.3 版本的样子。

640?wx_fmt=png

整个过程一目了然,list 是存放 闲置的Tcp连接 的链表,当一个 请求 千辛万苦到了这里,它要开始在链表的末尾开始 查找有没有可以用的 小跑车(Tcp连接),先把从小跑车 从 车库(list)里搬出来,然后检查下动力系统,轮子啥的,如果发现坏了( 当前连接不可用 ,已经被服务端关闭的,或者有异常数据的 等等 ), 你需要用把这个坏的车给砸了( 销毁Tcp连接 ),再去搬下一个小跑车。

如果可以用,那么很幸运,这个请求可以立刻开着小跑车去飙车(发送数据)。如果这个车库的车全是坏的或者一个车都没有,那么这个请求就要自己造一个小跑车 ( 建立新的TCP 连接 )。 这里还有一个点,小跑车数量是有限制的。假如轮到你了,你发现车库里没有车,你要造新车,但是系统显示车子数量已经达到最大限制了,所以你就要等 小伙伴 ( 别的请求 ) 把 小跑车用完后开回来,或者等车库里的坏车 被别的小伙伴砸了。

整个过程看起来好像也挺高效的,但是请注意 lock (SyncObj) 上述所有操作的都被上锁了,这些操作同时只能有一个小伙伴操作,这样做的原因当然是为了安全,防止两个请求同时用了同一个Tcp连接,这样的话车子会被挤坏掉的。 于是小伙伴们都一个一个的排着队。 试想,当我们的请求很多很多的时候,队伍很长很长,那每个请求执行的时间久会变长。

那有没有什么方法可以加快速度呢? 其实是有的,事实上危险的操作 只是从 list 中去取车,和造新车。防止抢车和两个小伙伴造了同一个车。于是手术后的样子是这样的:

640?wx_fmt=png

可以看出,它把加锁执行的内容减少了,将检查车子的工作放到锁外。此外 将 lock...while 变成了while...lock 这样有什么影响呢:可以减少线程之间的竞争,如评论所说,lock...while 是霸道的,一线程阻塞,万线程等待竞争,而 while...lock 所有线程展开公平的竞争,大家持有锁几乎是相同的几率。

没想到这样一个操作,在Linux中提升了60% 的性能。减少了小伙伴之间的等待时间。

那么 静态的HttpClient 和 HttpClientFactory 的二者使用,哪个性能更好呢? 我认为是前者,在高并发的实验过程中也确实如此。因为 静态HttpClient 只有一个消息通道,从头用到尾,这样无疑是最高效的。而HttpClientFactory 需要销毁 HttpMessageHandle 销毁 HttpMessageHanlde 的过程是链条中的节点一个一个被摧毁的过程,直到最后的Tcp 连接池也被销毁。在使用Service.AddHttpClient 时需要设置生存周期,这就是HttpMessageHandle 的生存时长,我认为应该将其设置的长一些,这样HttpMessageHandle 或者叫做消息通道 就可以多多的被重复利用,因为HttpClientFactory 可以给不同的HttpClient注入相同的HttpMessageHandle

看完这篇文章 还可以看下这篇文章的姊妹篇:工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题

当然我遇到的问题 是否真的是因为 HttpClient 性能的提升而解决,现在也不能确定。还需要进一步检测验证。

相关文章:

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

  • .NET Core 中正确使用 HttpClient 的姿势

原文地址:https://www.cnblogs.com/dacc123/p/9892274.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/319176.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【线段树】Serious Business(CF1648D)

正题 luogu CF1648D 题目大意 有一个 3*n 的矩阵&#xff0c;1,3行没有行走限制&#xff0c;对于第2行&#xff0c;有m个区间&#xff0c;覆盖第 i 个区间有 kik_iki​ 的代价&#xff0c;只有覆盖的位置才能走&#xff0c;让你从 (1,1) 走到 (3,n)&#xff08;只能向下和向右…

P6378-[PA2010]Riddle【2-SAT】

正题 题目链接:https://www.luogu.com.cn/problem/P6378 题目大意 给出nnn个点mmm条边的一张无向图&#xff0c;图中有kkk种颜色的点。 要求每种颜色选择一个点作为关键点&#xff0c;满足每条边两边至少有一个关键点 求是否有满足的方案 1≤n,m,k≤1061\leq n,m,k\leq 10^…

后缀数组(讲解)

子串&#xff1a;从原串中选取连续的一段&#xff0c;即子串 空串也是子串 后缀&#xff1a;suf(k)为s(k…n)构成的子串 任何子串都是某个后缀的前缀 最长公共前缀 lcp(suf(i),suf(j)) 问题&#xff1a; 将所有后缀suf(1),suf(2),suf(N)按照字典序从小到大排序 暴力sort N2 …

codeforces1471 D. Strange Definition

D. Strange Definition 大佬题解 由lcm(x,y)xygcd(x,y)lcm(x,y)\frac{xy}{gcd(x,y)}lcm(x,y)gcd(x,y)xy​可知&#xff0c;如果lcm(x,y)gcd(x,y)xygcd2(x,y)\frac{lcm(x,y)}{gcd(x,y)}\frac{xy}{gcd^2(x,y)}gcd(x,y)lcm(x,y)​gcd2(x,y)xy​是完全平方数&#xff0c;那么xyxy…

2018 上海.NET职位围观报告

我一直说我是夏眠动物&#xff0c;如今已经11月份了&#xff0c;差不多也该活过来了&#xff0c;所以我决定写篇文章给各位.NET的支持者们和公司打打气&#xff0c;也算是为社区做点贡献吧。我最近主要干了两件事&#xff1a;让NPOI支持.NET Core&#xff0c;现已发布2.4版本。…

P5437-[XR-2]约定【拉格朗日差值,数学期望】

正题 题目链接:https://www.luogu.com.cn/problem/P5437 题目大意 nnn个点的完全图&#xff0c;连接i,ji,ji,j的边权值为(ij)k(ij)^k(ij)k。随机选出一个生成树&#xff0c;求期望边权和。 1≤n<998244353,1≤k≤1071\leq n<998244353,1\leq k\leq 10^71≤n<99824435…

【模板】分散层叠算法(P6466)

正题 P6466 题目大意 给你 k 个大小为 n 的数组&#xff0c;有q次询问&#xff0c;每次询问回答x在k个数组中的后继的异或和 解题思路 分散层叠模板 code #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll …

后缀数组(后续)

文章目录**后缀数组 Height**两个子串最长公共前缀**求Height数组**比较一个字符串的两个子串的大小关系不同子串的数目出现至少k次的子串的最大长度**总结&#xff1a;**代码&#xff1a;后缀数组 Height 利用后缀数组快速求出2个后缀的lcp长度 lcp:最长公共前缀 lcp(suf(i),…

codeforces1472 G. Moving to the Capital

G. Moving to the Capital 先bfs一边&#xff0c;求出距1号点的最短路用数组d1[]记录&#xff0c;求的过程中如果当前点t遍历到之前遍历过的点j意味着这条边就是能够拉近与1号点距离的边&#xff08;横向边或者后向边&#xff09;那么就用d1[j]更新d2[t]&#xff0c;d2[]表示最…

P3793-由乃救爷爷【分块,ST表】

正题 题目链接:https://www.luogu.com.cn/problem/P3793 题目大意 给出nnn个数字的一个序列mmm次询问区间最大值 保证数据随机 1≤n,m≤21071\leq n,m\leq 2\times 10^71≤n,m≤2107 解题思路 使用STSTST表可以做到O(1)O(1)O(1)询问&#xff0c;但是预处理的时空复杂度都是…

【DP】Mod Mod Mod(CF889E)

正题 CF889E luogu 题目大意 给你 n 个数&#xff0c;让你选择一个X&#xff0c;使得 ∑i1nXmoda1moda2...modai\sum_{i1}^nX\mod a_1\mod a_2...\mod a_i∑i1n​Xmoda1​moda2​...modai​ 最大 解题思路 可以发现必定存在一个 i &#xff0c;使得当前点贡献为 aia_iai​&a…

老张 .NetCore与Vue 框架学习

缘起作为一个.Net攻城狮已经4年有余了&#xff0c;一直不温不火&#xff0c;正好近来项目不是很忙&#xff0c;闲得无聊&#xff0c;搞一搞新技术&#xff0c;一方面是打发无聊的时间&#xff0c;一方面也是督促自己该学习辣&#xff01;身边的大神都转行的转行&#xff0c;加薪…

【平衡规划】Arithmetic Operations(CF1654E)

正题 CF1654E luogu 正题 给你一个正整数序列&#xff0c;你可以让一个位置变成任意整数&#xff0c;问你最少修改多少个数&#xff0c;能使得其成为等差序列 解题思路 考虑根号分治 对于公差小于 n\sqrt{n}n​ 的&#xff0c;直接枚举公差&#xff0c;然后枚举所有点&…

P1251-餐巾计划问题【费用流】

正题 题目链接:https://www.luogu.com.cn/problem/P1251 题目大意 NNN天&#xff0c;第iii天需要aia_iai​个餐巾。 每个餐巾价格为ppp&#xff0c;使用完后有两种清洗方法 清洗mmm天&#xff0c;费用为fff清洗nnn天&#xff0c;费用为sss 求满足所有需求的最小花费 1≤N≤…

J. Spy(多重匹配KM算法)

J. Spy 随机打&#xff0c;最后答案乘n&#xff0c;因为我方等概率的遇见敌人&#xff0c;相当于与n个敌人都打一遍&#xff0c;然后贡献累加作为匹配边权 bfs版本的KM #include<cstdio> #include<cstring> #include<iostream> #include<algorithm>…

2018年10月28日宁波dotnet社区活动回顾及下次活动预告

离上次活动&#xff0c;有半年了&#xff0c;汗。之后尽量保证每月一次&#xff0c;以组织为主&#xff0c;多邀请嘉宾来分享。本次活动不足之处人手不足&#xff1a;由于活动组织事项受限于人手&#xff08;目前就我一个&#xff0c;这次活动前后我又应邀给大红鹰学院应届生介…

[JSOI2007]字符加密

题目描述 喜欢钻研问题的JS 同学&#xff0c;最近又迷上了对加密方法的思考。一天&#xff0c;他突然想出了一种他认为是终极的加密办法&#xff1a;把需要加密的信息排成一圈&#xff0c;显然&#xff0c;它们有很多种不同的读法。 例如‘JSOI07’&#xff0c;可以读作&…

【状压DP】滚榜(P7519)

正题 P7519 题目大意 n个队伍&#xff0c;排名先按分数排序再按编号排序&#xff0c;每个队伍有一个初始分数 aia_iai​&#xff0c;和一个附加分数 bib_ibi​ 对于一个合法的 bib_ibi​ 序列&#xff0c;按 bib_ibi​ 大小排序&#xff0c;从小到大把每个 bib_ibi​ 加进对…

P4480-[BJWC2018]餐巾计划问题【三分,贪心】

正题 题目链接:https://www.luogu.com.cn/problem/P4480 题目大意 nnn天&#xff0c;第iii天需要aia_iai​个餐巾。 每个餐巾价格为ppp&#xff0c;使用完后有两种清洗方法 清洗m1m_1m1​天&#xff0c;费用为c1c_1c1​清洗m2m_2m2​天&#xff0c;费用为c2c_2c2​ 求满足所…

BotSharp v0.2 发布, 支持微信智能回复

BotSharp v0.2 主要是针对微信的消息平台做整合&#xff0c;让.NET开发者可以轻松的搭建基于NLU自然语言理解的智能回复功能&#xff0c;BotSharp.Channel.Weixin模块负责和微信的公众号平台对接&#xff0c;接收消息通知&#xff0c;并能消息产生智能回复&#xff0c;回复的内…