把HttpClient换成IHttpClientFactory之后,放心多了

Code综艺圈 2021-06-18 09:06:16
HttpClient 换成 之后 放心 ihttpclientfactory


前言

关于HttpClient的使用,个人在很多场景都派上用场了,比如在Winform或后台服务中用其调用接口获取和上传数据、微服务中用其进行各服务之间的数据共享等,到目前来看,似乎还没有出现过什么问题,但当我看到官方文档介绍使用方式时,再回顾之前项目的代码,只能说没出问题比较庆幸。

官方文档介绍的大概意思如下:

HttpClient类使用比较简单,但在某些情况下,许多开发人员却并未正确使用该类;虽然此类实现 IDisposable,但在 using 语句中声明和实例化它并非首选操作,因为释放 HttpClient 对象时,基础套接字不会立即释放,这可能会导致套接字耗尽问题,最终可能会导致 SocketException 错误。要解决此问题,推荐的方法是将 HttpClient 对象创建为单一对象或静态对象

看到这,有一点点小不安(因为有些项目就是用using的方式),虽然目前的并发量还不至于导致SocketException异常的发生,但优化得马不停蹄的安排上;先来探探HttpClient,再来说说IHttpClientFactory。

正文

1. HttpClient好像一直没用对

这里创建了一个控制台程序进行测试(.NetCore3.1),代码比较简单,如下:

image-20210612233306352

注:代码中访问的地址是用Nginx搭建在阿里云上搭建的站点,没有做负载,所以看Socket状态比较直观。

上面代码执行完成之后就退出了,理论情况来说,上面程序执行完毕之后,就不应该占用资源啦,但通过netstat查看,的确还有Socket被占用,测试如下:

  • Windows 测试

    执行如下命令看结果:

    netstat -ano | findstr TIME_WAIT
    

    image-20210612234018012

  • Linux测试

    我的云服务器之前就把.NetCore运行环境安装好了,所以创建一个目录,将编译之后的文件通过Xftp将文件传到云服务器,执行以下命令即可:

    # 注意,这里指定启动的是dll文件
    dotnet HttpClientConsoleDemo.dll
    

    image-20210613000123727

    查看端口情况,执行以下命令即可:

    netstat -ant | grep TIME_WAIT # 查看端口占用情况,找到状态为TIME_WAIT
    

    image-20210613000449843

    TIME_WAIT 是主动关闭 TCP 连接的那一方出现的状态,系统会在 TIME_WAIT 状态下等待 2MSL(maximum segment lifetime )后才能释放连接(端口),目的是为了在TCP 四次挥手关闭连接机制中,保证 ACK 重发和丢弃延迟数据。按照解释来看,这种做法也算是合情合理,保证数据传输嘛,但的确就是占用资源啦;具体关于网络的相关知识,小伙伴们再去查阅一下。

经过Windows和Linux的测试,大概差不多两分钟的时间Socket才完全被释放,可想,如果是在高并发情况下,每台机器的能开的连接数是有限的,使用这种方式进行服务和服务之间交互数据,那肯定会出现问题。

而从理论上来讲,只要HttpClient继承了IDisposable接口,using块执行完就可以释放掉资源,而HttpClient确实是间接继承了IDisposable(直接继承HttpMessageInvoker ,而HttpMessageInvoker继承了IDisposable接口)。但在这里,使用HttpClient分配的Socket端口没有得到及时释放,为避免这个问题,官方推荐用静态变量的方式使用HttpClient。

2. HttpClient换种方式好多啦

按照官方建议,将HttpClient变量定义为静态变量,代码如下:

image-20210613224709180

运行程序,执行netstat命令可以看到,资源占用情况明显减少了。

如果使用静态变量,看似解决了对应的问题,但若想针对不同的请求设置不同的头信息时就显得不太方便,至于其他问题我暂时还没遇见过(官方说这种方式不支持 DNS 变更)。先忽略其他问题,在.NetCore2.1开始出现了IHttpClientFactory ,据说是可以解决之前HttpClient面临的一些问题,所以有需要用HttpClient的场景,直接用IHttpClientFactory 就妥啦,不信就来试试。

3. IHttpClientFactory用起来很给力

IHttpClientFactory是在.NETCore 2.1 开始提供的,默认实现为 DefaultHttpClientFactory ,专门用于创建在应用程序中用到的 HttpClient实例,自动维护内部的HttpMessageHandler池及其生命周期。主要功能如下:

  • 支持命名化、类型化配置,集中管理配置,避免冲突;
  • 出站请求管道配置灵活,轻松实现对请求生命周期的管理;
  • 合理管理内部HttpMessageHanlder的生命周期,避免资源占用和DNS刷新等问题
  • 内置日志记录器

接下来就来看看IHttpClientFactory到底有多给力;

3.1 控制台演示

由于内部需要使用了一些服务,并且是采用DI的形式注入的,所以首先要把依赖注入的相关的包引入进来,代码如下图:

image-20210614211552521

先来简单看一下需要注册的服务,后续会好好说:

image-20210614212416641

运行程序,然后通过以下命令查看端口占用情况;

netstat -ano | findstr TIME_WAIT # 根据状态去找
netstat -ano | findstr 47.113.204.41 # 根据IP去找

最终没有见到很明显的资源占用情况,有没有给力一点;还有一些比较常用的方式,在WebApi项目中一一演示(毕竟微服务中服务间通信还是比较常用的)。

3.2 WebApi项目演示
  • 创建一个API项目,注册上相关服务即可

    image-20210614234351869

  • 增加一个测试控制器和测试接口

    image-20210614234115603

  • 运行看结果

    image-20210614233646831

    在API项目中是不是使用很简单,因为项目本身就内置了依赖注入相关功能,直接注册上服务就可以使用;但这还体现不出有多给力,接下来继续看看其他扩展方式的使用。

3.3 使用命名和类型模式区分不同HttpClient

使用步骤与3.2是一样的,只是在注册服务的时候不太一样而已。

  • 命名模式

    注册服务时代码如下:

    image-20210615001230877

    增加一个测试接口,运行结果和上面一样,只是HttpClient实例带的头信息不一样啦,如下:

    image-20210615001538406

  • 类型模式

    其实类型模式原理和命名模式是一样的,只是通过指定的类型名称作为对应HttpClient的名称,减少了单独定义名称的步骤,所以显得比较方便,个人比较喜欢这样用。

    首先定义一个业务处理类,直接使用HttpClient,代码如下:

    image-20210616174807761

    在Startup中注册服务时代码如下:

    image-20210616175313251

    增加一个测试接口,运行结果和上面一样,调用过程如下:

    image-20210616175603258

    进入TypeHttpClientService对应的方法,看到在注册时设置的头信息已经生效了,如下:

    image-20210616175932756

    执行完毕后,正常返回结果。类型模式不用单独为HttpClient起名,而是直接用指定类型的类名作为默认名,这样就相对方便啦;摘取获取类型名的源码如下:

    image-20210617091835065

    小扩展:自定义的管道类没有在Startup中手动注册,为什么能直接注入使用?

    答案:在services.AddHttpClient注册时,内部已经将对应Type注册好了,摘取代码如下:

    image-20210617091437591

    除了能轻松区分不同HttpClient之外,如果需要在请求过程中加入其它公共业务处理,可以通过增加自定义管道逻辑轻松实现。

3.4 在请求管道中增加自定义管道逻辑
  • 先定义一个管道类(CustomDelegatingHandler),继承DelegatingHandler,然后重写SendAsync方法

    image-20210616231200149

  • 在Startup中注册对应的服务,根据需要在HttpClient上加上自定义管道

    image-20210616231538671

  • 运行看效果,只有用到类型模式的HttpClient才会加入自定义管道,因为在配置的时候进行了针对性的配置

    image-20210616231900098

    这种方式是不是感觉和.NetCore的中间件管道很类似,编写自定义管道其实就类似于在编写一个中间件(至于原理,后续单独扒扒源码),轻松实现在请求前和响应后做相关业务处理,就像上面加的RequestID,对分析分布式事务及微服务调用链跟踪都有很大的帮助,所以小伙伴可以根据自己的需要封装自己想要的HttpClient。

4. IHttpClientFactory 搭配Polly就完美了

只要牵涉到网络,要想服务百分百稳定那是相当难,比如常遇到的网络不通、网络波动、远端服务挂了、远端服务响应慢等问题,都可能会影响用户对系统的体验,别慌,还记得之前说过专门为故障、弹性应变设计的Polly库吗(详情请看Polly-故障处理和弹性应对很有一手),如果HttpClientFactory能和Polly搭配使用岂不美哉;

其实是我们也能结合HttpClientFactory和Polly自己封装, 不过微软已经把轮子造好了,拿来用即可,如下步骤:

4.1 引入对应版本的包

引入Microsoft.Extensions.Http.Polly包,这里使用的是.NetCore3.1,所以引入对应的版本为3.X都行。

4.2 Startup中注册服务的时候指定策略

image-20210617001659629

4.3 新增测试接口

image-20210617002008925

4.4 运行看效果

image-20210617001439859

在浏览器访问接口的时候,对比控制台打印的消息,会按照设定的时间间隔进行重试,效果很明显的。

对于IHttpClientFactory集成Polly的使用就先说到这,关于其他策略的演示,和单独使用Polly是一样的,相信小伙伴参照上面案例演示一定能搞定。对于Polly的其他策略使用,可以参考《Polly-故障处理和弹性应对很有一手》这篇文章,说的挺详细。

总结

IHttpClientFactory的应用暂时就先说到这,上面提到的功能只是平时自己常用的,其他功能小伙伴可以去探究探究;后续单独和小伙伴们一起扒扒源代码,整理整理一下流程。

如果现有项目需要用到HttpClient,建议通过IHttpClientFactory的方式,用起来方便、灵活,主要是是能避免一些问题。

博文源代码github地址:https://github.com/zyq025/DotNetCoreStudyDemo

一个被程序搞丑的帅小伙,关注"Code综艺圈",识别关注跟我一起学~~~

版权声明
本文为[Code综艺圈]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/zoe-zyq/p/14892435.html

  1. HTML + CSS + JavaScript to achieve cool Fireworks (cloud like particle text 3D opening)
  2. HTML + CSS + JavaScript realizes 520 advertising love tree (including music), which is necessary for programmers to express themselves
  3. Solve the problem of Web front-end deployment server (it can be deployed online without a server)
  4. HTML + CSS + JS make wedding countdown web page template (520 / Tanabata Valentine's Day / programmer advertisement)
  5. What else can driverless minibus do besides "Park connection"?
  6. Cloud native leads the era of all cloud development
  7. NRM mirror source management tool
  8. Bring it to you, flex Jiugong
  9. Lolstyle UI component development practice (II) -- button group component
  10. Deconstruction assignment in ES6
  11. Luo 2 peerless Tang clan was officially launched. The official gave a key point, and the broadcast time was implied
  12. 20初识前端HTML(1)
  13. 当新零售遇上 Serverless
  14. 20 initial knowledge of front-end HTML (1)
  15. When new retail meets serverless
  16. [golang] - go into go language lesson 5 type conversion
  17. [golang] - go into go language lesson 6 conditional expression
  18. HTML5(八)——SVG 之 path 详解
  19. HTML5 (8) -- detailed explanation of SVG path
  20. 需要开通VIP以后页面内容才能复制怎么办?控制台禁用javascript即可
  21. Web前端|CSS入门教程(超详细的CSS使用讲解,适合前端初学者)
  22. 实践积累 —— 用Vue3简单写一个单行横向滚动组件
  23. Serverless 全能选手,再下一城
  24. What if you need to open a VIP to copy the page content? Just disable JavaScript on the console
  25. Web front end | CSS introductory tutorial (super detailed CSS explanation, suitable for front-end beginners)
  26. Practice accumulation - write a single line horizontal scroll component simply with vue3
  27. Dili Reba is thin again. She looks elegant and high in a strapless hollow skirt, and her "palm waist" is beautiful to a new height
  28. Serverless all-round player, next city
  29. The difference between MySQL semi synchronous replication and lossless semi synchronous replication
  30. Vue表单设计器的终极解决方案
  31. The ultimate solution for Vue form designer
  32. Nginx从理论到实践超详细笔记
  33. Yu Shuxin's red backless swimsuit is split to the waist and tail, with a concave convex figure and excessive color matching, and his face is white to dazzling
  34. Nginx ultra detailed notes from theory to practice
  35. 【动画消消乐|CSS】086.炫酷水波浪Loading过渡动画
  36. typecho全站启用https
  37. CCTV has another popular employee. The off-site interpretation is very professional, and the appearance ability is no less than that of Wang Bingbing
  38. [animation Xiaole | CSS] 086. Cool water wave loading transition animation
  39. Enable HTTPS in Typecho
  40. 50天用JavaScript完成50个web项目,我学到了什么?
  41. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  42. What have I learned from completing 50 web projects with JavaScript in 50 days?
  43. "My neighbor doesn't grow up" has hit the whole network. There are countless horse music circles, and actor Zhou Xiaochuan has successfully made a circle
  44. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  45. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  46. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  47. 30 + women still wear less T-shirts and jeans. If they wear them like stars, they will lose weight
  48. 数栈技术分享前端篇:TS,看你哪里逃~
  49. Several stack technology sharing front end: TS, see where you escape~
  50. 舍弃Kong和Nginx,Apache APISIX 在趣链科技 BaaS 平台的落地实践
  51. Abandon the landing practice of Kong and nginx, Apache apisik on the baas platform of fun chain technology
  52. 浪迹天涯king教你用elementui做复杂的表格,去处理报表数据(合并表头,合并表体行和列)
  53. 前端HTML两万字图文大总结,快来看看你会多少!【️熬夜整理&建议收藏️】
  54. Wandering around the world king teaches you to use elementui to make complex tables and process report data (merge header, merge table body rows and columns)
  55. 路由刷新数据丢失 - vuex数据读取的问题
  56. Front end HTML 20000 word graphic summary, come and see how much you can【 Stay up late to sort out & suggestions]
  57. Route refresh data loss - vuex data reading problem
  58. Systemctl系统启动Nginx服务脚本
  59. Systemctl system startup nginx service script
  60. sleepless