用不完的好奇心 2019-12-12
在.Net Core应用开发中,调用第三方接口也是常有的事情,HttpClient使用人数、使用频率算是最高的一种了,在.Net Core中,HttpClient的使用方式随着版本的升级也发生了一些变化,本次就讲解一下Asp.Net Core2.1前后使用的两种方式。
一般来讲,喜欢要用的时候才会选择去获取资源,因此,当在有需求时才会用HttpClient去调用资源,便会使用如下这种方式或其它方式获取资源。
//do something... using (var httpClient = new HttpClient()) { var requestUri = "http://aspnetcore.online/api/resource/getresource"; var httpResponseMessage = await httpClient.GetAsync(requestUri); //do something... return Ok(httpResponseMessage); }
如果可以正常访问目标地址的话,则会返回相应的资源信息。
又如Post方式提交并返回相应的内容,都是可以直接使用。
//do something... using (var httpClient = new HttpClient()) { var requestUri = "http://aspnetcore.online/api/resource/postresource"; var httpResponseMessage = await httpClient.PostAsJsonAsync(requestUri,"星城软件"); //do something... return Ok(httpResponseMessage); }
但是这种情况下会出现一个严重的问题,在不停的调用情形下,tcp连接数会被耗尽,虽然使用using方式调用HttpClient并在退出前调用Dispose()方法将HttpClient释放了,但是tcp连接仍然处于保持状态,在240s后才会自动断开,这里就涉及到一个连接状态了,首先得理解下http的工作原理,http协议是建立在tcp协议基础之上,当浏览器需要从服务器获取数据的时候,会发出一次http请求。http会通过tcp建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,http会立即将tcp连接断开,这个过程是很短的。所以http连接是一种短连接,是一种无状态的连接。但是tcp的连接只要我们不通过代码把连接关闭,这个连接就会在客户端和服务端的进程中一直存在,相关状态数据会一直保存着,直到无响应状态持续了默认关闭时间后自动断开。
当短期请求量过大时,这就可能导致了"套接字资源耗尽异常",因此,为了解决这个问题,想到不释放HttpClient,将它作为单例一直使用,实现单例方式有很多种。
如使用单例模式,只生成一个HttpClient
private static HttpClient _httpClient = null; public HttpClient CreateHttpClient() { if (_httpClient == null) _httpClient = new HttpClient(); return _httpClient; }
亦或是在初始化时完成单例注入,创建一个IHttpClient接口,及相应的实现StandardHttpClient,实现类种加入HttpClient属性,在实现类构造函数中完成初始化后便可直接使用该实现类完成资源请求工作。
//在startup中完成单例注入 services.AddSingleton<IHttpClient, StandardHttpClient>(); public interface IHttpClient { //do something } public class StandardHttpClient : IHttpClient { private HttpClient _client; public StandardHttpClient() { _client = new HttpClient(); } //do something... }
虽然这样解决了"套接字资源耗尽异常",但是又带来了新的问题,熬不过DNS生存时间(TTL),当主机 DNS 更新时,又可能产生异常,提示无法解析主机名称,因为单例HttpClient不会随着主机DNS更新而更新,Singleton HttpClient doesn‘t respect DNS changes。
An error occurred while sending the request. Couldn‘t resolve host name An error occurred while sending the request. Couldn‘t resolve host name
在.Net Core2.1后,微软引入了HttpClientFactory彻底解决这个问题,工厂模式的职责是负责创建对象,这个类主要负责创建HttpClient实例
首先在StartUp中注册,可能会提示安装这个Nuget包
services.AddHttpClient();
该方法内部实现过程可以浏览:https://www.cnblogs.com/lizhizhang/p/9502862.html
其次,在需要使用时,使用构造函数注入即可
[Route("api/[controller]")] [ApiController] public class HttpClientController : ControllerBase { IHttpClientFactory _httpClientFactory; public HttpClientController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } [HttpGet] [Route(nameof(Index))] public async Task<IActionResult> Index() { var client = _httpClientFactory.CreateClient(); var result = await client.GetAsync("http://aspnetcore.online/api/resource/getresource"); return Ok(result); } }
具体实现原理简述为:HttpClientFactory内部管理着一个连接句柄池,对每一个HttpClient使用一个句柄进行跟踪管理,当该实例使用完毕后,句柄仍然控制资源释放,在短期大量处理时,可以将这部分句柄完成对不同实例的跟踪管理,使得句柄,也就是相应的套接字生命周期延长,对套接字完成了复用。
近日,长沙.NET技术社区已经建立,微信群:长沙.NET社区一群已满,如有需要来长沙发展或是回归长沙可以进入二群,加一下我来邀请进去。各大城市间的人才拉锯战进行中,不管是什么行业,什么职业,长沙都应留住人才,培养人才。
2019-02-26,望技术有成后能回来看见自己的脚步