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

前言

在 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"

你会发现本地开启了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,请赶快试试新的方案吧。

总结

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

(0)

相关推荐

  • ASP.NET MVC Web API HttpClient简介

    1.HttpClient简单介绍 依稀还记得那个时候用WebClient,HttpWebRequest来发送一个请求,现在ASP.NET MVC4中自带了一个类HttpClient,用于接收HttpResponseMessage和发送HttpRequestMesssage. 问题在于既然WebClient,HttpWebRequest可以完成相应的功能,为什么还要使用HttpClient类,.NET Framework中既然提出了这样一个类肯定是有其特别之处的,这里罗列几个不同之处: (1) 可

  • ASP.NET Core针对一个使用HttpClient对象的类编写单元测试详解

    介绍 几年前,微软引入了HttpClient类来替代HttpWebRequest来发送Web请求.这个新的类更易于使用,更加简洁,更具有异步性,且易于扩展. HttpClient类有一个可以接受HttpMessageHandler类对象的构造函数.HttpMessageHandler类对象可以接受一个请求(HttpRequestMessage), 并返回响应(HttpResponseMessage).它的功能完全取决于它的实现.默认情况下HttpClient使用的是HttpClientHandl

  • 通过HttpClient 调用ASP.NET Web API示例

    在前面两篇文章中我们介绍了ASP.NET Web API的基本知识和原理,并且通过简单的实例了解了它的基本(CRUD)操作.我们是通过JQuery和Ajax对Web API进行数据操作.这一篇我们来介绍一下使用HttpClient的方式来对Web API进行数据操作. 这里我们还是继续使用对Product的操作实例来演示一下它的基本应用. 创建ASP.NET Web API应用程序 在VS中选择创建一个ASP.NET Web Application应用程序,在向导的下一个窗口中选择Web API

  • C# HttpClient Cookie验证解决方法

    自实现的cookie 验证,远程取值的例子 以下代码配合HttpClient使用可以实现跨域(cookie的读写) //验证 复制代码 代码如下: HttpClient httpClient = new HttpClient(url, null, true); httpClient.PostingData.Add(key,value);//登录用户名 httpClient.PostingData.Add(key,value);//密码 string str = httpClient.GetStr

  • Asp.Net Core2.1前后使用HttpClient的两种方式

    前言 在.Net Core应用开发中,调用第三方接口也是常有的事情,HttpClient使用人数.使用频率算是最高的一种了,在.Net Core中,HttpClient的使用方式随着版本的升级也发生了一些变化,本次就讲解一下Asp.Net Core2.1前后使用的两种方式. 一.原先HttpClient使用方式 一般来讲,喜欢要用的时候才会选择去获取资源,因此,当在有需求时才会用HttpClient去调用资源,便会使用如下这种方式或其它方式获取资源. //do something... usin

  • .NET Core 2.1中HttpClientFactory的最佳实践记录

    前言 ASP.NET Core 2.1中出现一个新的HttpClientFactory功能, 它有助于解决开发人员在使用HttpClient实例从其应用程序发出外部Web请求时可能遇到的一些常见问题. 介绍 在.NETCore平台的2.1新增了HttpClientFactory,虽然HttpClient这个类实现了disposable,但使用它的时候用声明using包装块的方式通常不是最好的选择.处理HttpClient,底层socket套接字不会立即释放.该HttpClient类是专为多个请求

  • 谈谈HttpClient使用详解

    HttpClient是一个客户端的HTTP通信实现库.HttpClient的目标是发送和接收HTTP报文. Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性.因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议

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

    前言 为了更方便在服务端调用 HTTP 请求,微软在 .NET Framework 4.x 的时候引入了 HttpClient.但 HttpClient 有很多严重问题,一直饱受诟病,比如 InfoQ 的这篇文章 t.cn/Evzy80y,吐槽了 HttpClient 不能立即关闭连接.性能消耗严重等的问题. Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅是客户端发送Http请求变得容易,而且也方便了开发人员

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

    前言 在 Asp.Net Core 1.0 时代,由于设计上的问题, HttpClient 给开发者带来了无尽的困扰,用 Asp.Net Core 开发团队的话来说就是:我们注意到,HttpClient 被很多开发人员不正确的使用.得益于 .Net Core 不断的版本快速升级: 问题来源 长期以来,.NET开发者都通过下面的方式发送http请求: using (var httpClient = new HttpClient()) { var response = await httpClien

  • python中四舍五入的正确打开方式

    round()函数 (注意:下面的我也不清楚是否正确,我只是发表一下我的观点) 对于简单的舍入运算,使用内置的 round(value, ndigits) 函数即可 强烈建议不要去深究,就直接得结果就好 ndigiths可以为正数,也可以为负数,还可以为0,可以为空 n:就是精确到第n位小数,对整数没有影响,1为精确到十分位(注意:小数就是从十分位往后推的) -n:就是精确到整数位,-1为精确到十位,然后就是百位千位-有小数位就全舍掉,不管多大,但会保留一个为0的小数位 0:精确到个位,但会保留

  • TypeScript中extends的正确打开方式详解

    目录 前言 extends第一式:继承 类继承类 接口继承接口 接口继承类 extends第二式:三元表达式条件判断 普通的三元表达式条件判断 情况一:Type1和Type2为同一种类型. 情况二:Type1是Type2的子类型. 情况三: Type2类型兼容类型Type1. 带有泛型的三元表达式条件判断 extends第三式:泛型约束 前言 最近完整地看了一遍TypeScript的官方文档,发现文档中有一些知识点没有专门讲解到,或者是讲解了但却十分难以理解,因此就有了这一系列的文章,我将对没有

  • vue-cli + sass 的正确打开方式图文详解

    关于在vue-cli搭建的项目中怎么配置sass,网上搜到的基本是这种答案: 但是我认为,直接将样式写在每个单文件的<style>里,是十分不明智的做法.且不说node-sass安装过程的各种坑,内嵌的<style>也让组件显得十分混乱.想象一下你在修改某个methods时必须拖动滚轮穿越几十上百行的css代码,又或者为了修改一组样式,却找不到对应的css文件,因为它们散布在vue文件里... 在我看来,正确的做法应该是单独管理sass文件,然后在main.js中直接引入编译好的c

  • 基于python的多进程共享变量正确打开方式

    多进程共享变量和获得结果 由于工程需求,要使用多线程来跑一个程序.但是因为听说python的多线程是假的,于是使用多进程,反正任务需要共享的参数少. 查阅资料,发现实现多进程主要使用Multiprocessing,有两种方式,一种是Process,另一种是Pool. p = Process(target=fun,args=(args)) 再通过p.start()来启动一个子进程,通过p.join()方法来使得子进程运行结束后再执行父进程. 但是这样很烦,还要写个for 循环来开n个线程和join

  • 云开发 VSCode 插件 Cloudbase Toolkit 的正确打开方式及应用场景分析

    什么是 Cloudbase Toolkit Tencent CloudBase Toolkit 是云开发的 VS Code(Visual Studio Code)插件.该插件可以让您更好地在本地进行云开发项目开发和代码调试,并且轻松将项目部署到云端. Cloudbase Toolkit 将项目创建.函数上传.函数更新.函数本地调试等功能集成在 VSCode 的本地调试环境中,开发者可以通过简单的点击,完成云函数的更新.上传.同步等功能. 和 Cloudbase Cli 相比,Cloudbase

  • Spring原生Rpc六种的正确打开方式实现示例

    目录 前言 什么是Rpc? Spring中的Rpc 定义服务接口 调用服务代码 WEBSERVICE的RPC实现 服务提供者 服务实现 服务暴露 服务消费者 HTTP的RPC实现 服务提供者 服务实现 服务暴露 服务消费者 文末结语 前言 在java生态圈谈到Rpc,很多人可能就会想到Dubbo.Motan.Grpc等框架.但是你知道吗?作为Java编程全家桶的Spring已经内置了多种RPC的实现方式,可以直接使用.存在即合理,有些场景下其实并不需要Dubbo,Grpc等重量级的RPC组件,那

  • 基于Python log 的正确打开方式

    保存代码到文件:logger.py import os import logbook from logbook.more import ColorizedStderrHandler import smtplib LOG_DIR = os.path.join('log') if not os.path.exists(LOG_DIR): os.makedirs(LOG_DIR) def get_logger(name='test', file_log=False): logbook.set_date

  • 在Pycharm中修改文件默认打开方式的方法

    新下载了一个Pycharm,建了个小demo,期间产生了一个sqlite3文件,由于是第一次打开,就弹出选择打开方式的对话框,手一块直接点了个Text,然后就乱码了: 那我们不小心操作后,怎么重新修改文件的默认打开方式呢? 1.File -> Settings -> Editor -> File Types 找到Text下的"*.sqlite3",把它删掉就ok啦~ 以上这篇在Pycharm中修改文件默认打开方式的方法就是小编分享给大家的全部内容了,希望能给大家一个参

  • Java Gradle项目中的资源正确获取方式

    引言 一个Java Gradle项目会涉及到资源的访问. 一般情况下会将当前项目所需的资源文件全部放置于resources文件夹下, 无论是main文件下的source code 还是test文件夹下的test code. 都或多或少的涉及到获取resources文件夹下的资源. 本文主要目的就是详细的总结一下如何获取resources文件夹下的资源. 两个getResource方法 来看一个简单的Java Gradle项目(称呼其为simpleresource)的项目结构 首先这个projec

随机推荐