欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-10 09:56:34
前言 在 asp.net core 1.0 时代,由于设计上的问题, httpclient 给开发者带来了无尽的困扰,用 asp.net core 开发团队的话来...

前言

在 asp.net core 1.0 时代,由于设计上的问题, httpclient 给开发者带来了无尽的困扰,用 asp.net core
开发团队的话来说就是:我们注意到,httpclient 被很多开发人员不正确的使用。得益于 .net core
不断的版本快速升级;

问题来源

长期以来,.net开发者都通过下面的方式发送http请求:

using (var httpclient = new httpclient())
{
 var response = await httpclient.getasync(uri);
  
 //do something with response
}

这段代码理论上来说遵守了c#的最佳实践,httpclient是idisposable类型,所以我们通过using语法糖来使用httpclient。微软官方的文档也提到:

as a rule, when you use an idisposable object, you should declare and instantiate it in a using statement

可是,当我们试图运行下面的测试:

public async task sendrequest() 
{
 console.writeline("starting reqeust");
 for(int i = 0; i<10; i++)
 {
  using(var client = new httpclient())
  {
   var result = await client.getasync("http://www.baidu.com");
   console.writeline(result.statuscode);
  }
 }
 console.writeline("reqeust done");
}

此时在terminal下列出所有端口:

netstat -ap tcp | grep -i "time_wait"

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

你会发现本地开启了10个端口,这说明httpclient的工作原理其实跟我们认为的idisposable是有区别的,如果你把httpclient用作大规模的http请求,实际上会创建很多个http连接,而且这些资源并不能被立即释放。一个显而易见的改进方法是共享同一个httpclient实例,从而达到节约socket资源的目的。

private static readonly httpclient _client = new httpclient();
public async task sendrequest2() 
{
 _testoutputhelper.writeline("start request");
 
 for(int i = 0; i<10; i++)
 {
  var result = await _client.getasync("http://www.baidu.com");
  console.writeline(result.statuscode);
 }
 _testoutputhelper.writeline("request done");
}

这个方案似乎解决了问题,使用单例的httpclient的确会减少socket资源,但是这个方案会引发新的问题:由于这个http连接始终保持连接状态,所以当请求地址的dns发生更新的时候并不会应用到这个http连接上。这个问题在微服务,高可用时代更加常见singeton httpclient doesn't respect dns changes

最终,一个叫做httpclientfactory的开源实现用来彻底解决这个问题。微软也将httpclientfactory集成在了.net core中。

在.net core中创建httpclient
1.添加nuget

microsoft.extensions.http 

2.在dependency injection容器中注册服务

services.addhttpclient();

3. 使用构造器注入使用ihttpclientfactory

public class basicusage
{
 private readonly ihttpclientfactory _clientfactory;

 public basicusage(ihttpclientfactory clientfactory)
 {
  _clientfactory = clientfactory;
 }

 public async task sendrequest()
 {
  var request = new httprequestmessage(httpmethod.get, 
   "http://www.baidu.com");

  var client = _clientfactory.createclient();
  var response = await client.sendasync(request);
  //do something for response
 }
}

4. 使用named httpclient

由于我们在di容器中注册了唯一的httpclientfactory,意味着通过httpclientfactory创建出来的httpclient可能是同一个配置和参数,如果你需要不同配置的httpclient,你可以通过“起名字的”的方式注册不同的httpclient。

services.addhttpclient("baidu", c =>
{
 c.baseaddress = new uri("https://www.baidu.com");
 c.defaultrequestheaders.add("accept", "application/json");
});

一旦注册了一个名叫“baidu"的httpclient,你就可以通过下面的方式来建创建httpclient:

var client = _clientfactory.createclient("baidu");

5.集成polly

polly是一个用来故障处理库,它允许开发者在http请求中添加“重试、熔断器、超时等”策略。

先添加nuget:

microsoft.extensions.http.polly 

添加策略:

var timeout = policy.timeoutasync<httpresponsemessage>(timespan.fromseconds(10));

services.addhttpclient("baidu")
 .addpolicyhandler(request => timeout)
 .addtransienthttperrorpolicy(p=>p.retryasync(3));

当然还有一些高阶用法,详见initiate http requests,总之httpclientfactory提供了一种高效实用httpclient的方式,如果你还在自己new httpclient,请赶快试试新的方案吧。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。