HttpClient 日常使用及坑点:

在 C# 中,平时我们在使用 HttpClient 的时候,会将 HttpClient 包裹在 using 内部进行声明和初始化,如:

using(var httpClient = new HttpClient())
{
//other codes
}

至于为什么?无外乎是:项目代码中就是这样写的,依葫芦画瓢/别人就是这样用的/在微软官方的 ASP.NET 教程中也是这么干的。

说的技术范点:当你使用继承了 IDisposable 接口的对象时,建议在 using 代码块中声明和初始化,当 using 代码段执行完成后,会自动释放该对象而不需要手动进行显示 Dispose 操作。

但这里,HttpClient 这个对象有点特殊,虽然继承了 IDisposable 接口,但它是可以被共享的(或者说可以被复用),且线程安全。从项目经验来看,倒是建议在整个应用的生命周期内,复用 HttpClient 实例,而不是每次 RPC 请求的时候就实例化一个。(之前在优化公司一个 web 项目的时候,也曾经因为 HttpClient 载过一次坑,后面我会进行简述。)

我们先来用个简单的例子做下测试,看为什么不要每次 RPC 请求都实例化一个 HttpClient:

  public class Program
{
static void Main(string[] args)
{
HttpAsync();
Console.WriteLine("Hello World!");
Console.Read();
} public static async void HttpAsync()
{
for (int i = 0; i < 10; i++)
{
using (var client = new HttpClient())
{
var result = await client.GetAsync("http://www.baidu.com");
Console.WriteLine($"{i}:{result.StatusCode}");
}
}
}
}

运行项目输出结果后,通过 netstate 查看下 TCP 连接情况:

  • 虽然项目已经运行结束,但是连接依然存在,状态为 "TIME_WAIT"(继续等待看是否还有延迟的包会传输过来。)。

默认在 windows 下,TIME_WAIT 状态将会使系统将会保持该连接 240s。

  • 这里也就引出了我上面说的载过的一次坑:在高并发的情况下,连接来不及释放,socket 被耗尽,耗尽之后就会出现喜闻乐见的一个错误:
#使用jemter压测复现错误信息:
Unable to connect to the remote serverSystem.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.

说白话:就是会出现“各种套接字问题”。(码WCF的童鞋可能更加记忆尤新,问题追根溯源都是换汤不换药。)

熊厂里面能够搜索出来的解决方法,基本都是“减少超时时间”,但人为减少了超时时间会出现各种莫名其妙的错误。且无法避免服务器迟早崩溃的问题。

可以通过注册表进行修改默认值:[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay])

那么如何处理这个问题?答案已经在上面说了,“复用 HttpClient”即可。如:

    public class Program
{
private static readonly HttpClient _client = new HttpClient();
static void Main(string[] args)
{
HttpAsync();
Console.WriteLine("Hello World!");
Console.Read();
} public static async void HttpAsync()
{
for (int i = 0; i < 10; i++)
{
var result = await _client.GetAsync("http://www.baidu.com");
Console.WriteLine($"{i}:{result.StatusCode}");
}
}
}

  • 可以看到,原先 10 个连接变成了 1 个连接。(请不要在意两次示例的目标 IP 不同---SLB 导致的,都是百度的ip)

  • 另外,因为复用了 HttpClient,每次 RPC 请求的时候,实际上还节约了创建通道的时间,在性能压测的时候也是很明显的提升。曾经因为这一举动,将 web 项目的 TPS 从单台 600 瞬间提升到了 2000+,页面请求时间也从 1-3s 减少至 100-300ms,甚是让测试组小伙伴膜拜(当然也包括了一些业务代码的细调。),但知道个中缘由后,一个小改动带来的项目性能提升。。。会让人上瘾:)

  • 至于如何创建一个静态 HttpClient 进行复用,大家可以按项目实际来,如直接创建一个“全局”静态对象,或者通过各类 DI 框架来创建均可。

但这么调整 HttpClient 的引用后,依然存在一些问题可能会影响到你的项目(尚未影响到我:P),如:

  • 因为是复用的 HttpClient,那么一些公共的设置就没办法灵活的调整了,如请求头的自定义。
  • 因为 HttpClient 请求每个 url 时,会缓存该url对应的主机 ip,从而会导致 DNS 更新失效(TTL 失效了)

那么有没有办法解决 HttpClient 的这些个问题?直到我遇到了 HttpClientFactory,瞬间写代码幸福感倍升,也感慨新时代的童鞋们真的太幸福了,老一辈踩的坑可以“完美”规避掉了。

HttpClientFactory 优势:

HttpClientFactory 是 ASP.NET CORE 2.1 中新增加的功能。

  • “完美”解决了我多年来遇到的这些坑,可以更加专注于业务代码。
  • HttpClientFacotry 很高效,可以最大程度上节省系统 socket。(“JUST USE IT AND FXXK SHUT UP”

    HttpClientFactory 使用说明 及 对 HttpClient 的回顾和对比的更多相关文章

    1. ASP.Net Core2.1中的HttpClientFactory系列一:HttpClient的缺陷

      引言: ASP.NET Core2.1 中出现了一个新的 HttpClientFactory 功能, 它有助于解决开发人员在使用 HttpClient 实例从其应用程序中访问外部 web 资源时可能遇 ...

    2. HTTPClient和HttpURLConnection实例对比

      HttpURLConnection是java的标准类,什么都没封装. HTTPClient是个开源框架,封装了访问http的请求头,参数,内容体,响应等等. 简单来说,HTTPClient就是一个增强 ...

    3. Asp.Net Web API 2第四课——HttpClient消息处理器

      Asp.Net Web API 导航   Asp.Net Web API第一课:入门http://www.cnblogs.com/aehyok/p/3432158.html Asp.Net Web A ...

    4. 【ASP.NET Web API教程】3.4 HttpClient消息处理器

      原文:[ASP.NET Web API教程]3.4 HttpClient消息处理器 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. 3.4 ...

    5. HttpClient在.NET Core中的正确打开方式

      问题来源 长期以来,.NET开发者都通过下面的方式发送http请求: using (var httpClient = new HttpClient()) { var response = await ...

    6. ASP.NET Core中如何针对一个使用HttpClient对象的类编写单元测试

      原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core? ...

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

      .net core 于 10月17日发布了 ASP.NET Core 2.2.0 -preview3,在这个版本中,我看到了一个很让我惊喜的新特性:HTTP Client Performance Im ...

    8. HttpClient参观记:.net core 2.2 对HttpClient到底做了神马

      .net core 于 10月17日发布了 ASP.NET Core 2.2.0 -preview3,在这个版本中,我看到了一个很让我惊喜的新特性:HTTP Client Performance Im ...

    9. 从一个舒服的姿势插入 HttpClient 拦截器技能点

      马甲哥继续写一点大前端,阅读耗时5 minute,行文耗时5 Days 今天我们来了解一下如何拦截axios请求/响应? 这次我们举一反三,用一个最舒适的姿势插入这个技能点. axios是一个基于 p ...

    随机推荐

    1. vs 编译说明

      静态编译/MT,/MTD 是指使用libc和msvc相关的静态库(lib).   动态编译,/MD,/MDd是指用相应的DLL版本编译.   其中字母含义  d:debug    m:multi-th ...

    2. 谈谈NOSQL

      从MongoDB引到NOSQL 要讲MongoDB之前,首先要提到一个概念NOSQL(NoSQL = Not Only SQL ) 很大一部分数据是由关系型数据库管理系统(RDMBSs)来处理的,关系 ...

    3. 源码阅读 - java.util.concurrent (二)CAS

      背景 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. ...

    4. React躬行记(7)——表单

      表单元素是一类拥有内部状态的元素,这些状态由其自身维护,通过这类元素可让用户与Web应用进行交互.HTML中的表单元素(例如<input>.<select>和<radio ...

    5. js random获取随机数,获取任意范围内随机整数

       壹 ❀ 引 想着好久没做笔试题了,去GitHub找了面试相关的项目,结果被第一道题难住了.....说难其实也不难,而是我忘记了取范围随机整数怎么写了,不可否认如果当时是我在笔试,肯定也凉了,那么就由 ...

    6. windows美化工具7+ Taskbar Tweaker

      今天分享一个windows美化工具 7+ Taskbar Tweaker 调整工具专为 Windows 任务栏工作者量身定制,支持 Windows 7 以及更高版本的(非服务器版)微软操作系统平台. ...

    7. 5分钟完成mysql离线安装

      1. 场景描述 mysql离线安装并不复杂,就是经常会出现漏东西,有时候的搞半天,总结下,快速离线安装mysql,直接把下面的命令敲一遍就好,5-10分钟就能安装好. 2. 解决方案 安装的mysql ...

    8. Hash的应用2

      代码: #include <stdio.h> #define OFFSET 500000//偏移量 ];//记录每个数是否出现,出现为1,不出现为0 int main(){ int n,m ...

    9. C语言指针专题——指针难学的4点原因

      前一篇跟大家聊了聊指针的概念,可是就算了解了指针是什么,为什么依然感觉难学?我试着从几个点切入,聊聊指针难学之处. 文末会给大家推荐几本书,有需要的朋友可以看看! 难点1. 讨厌的星号 定义指针变量p ...

    10. 数据库中 ’’ 和 NULL的区别

      null不是对象,''是对象 从'',你就可以知道这是一个字符串类型的数据,是一个长度为零的字符串. 从NULL,你只能知道这里没有赋过值,是空的,他不属于任何数据类型. 我们在数据库实际使用中,一般 ...