使用HttpClient实现并发请求
在.Net 4.0之前,一直是依靠HttpWebRequest实现Http操作的。它默认有一个非常保守的同一站点下最大2并发数限制,导致默认情况下HttpWebRequest往往得不到理想的速度,必须修改App.config或ServicePointManager.DefaultConnectionLimit的值。所以对于需要高并发请求的场景HttpWebRequest不是一个理想的选择。
MS在.Net 4.5中引入了一个HttpClient类专门处理Http操作,HttpClient不受HttpWebRequest并发策略控制,也没有系统级的并发限制。
关于HttpClient和HttpWebRequest的一些区别参考(https://stackoverflow.com/questions/22214930/httpclient-vs-httpwebrequest)
下面开始进入重点,在本文之前对于HttpClient有过很多错误的使用,最开始使用的如下代码:
写一个静态方法,每次调用该方法都会new一个新的HttpClient对象,使用完成后释放HttpClient对象
public static HttpResponseMessage GetRequest(string requestUri, string accessToken)
{
using (HttpClient client = new HttpClient())
{
if (!string.IsNullOrEmpty(accessToken))
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
}
client.DefaultRequestHeaders.Add("Accept", "application/json");
try
{
var response = client.GetAsync(requestUri).Result;
if (response.IsSuccessStatusCode)
{
return response;
}
else
{
throw new Exception(response.Content.ReadAsStringAsync().Result);
}
}
catch (Exception ex)
{ throw ex;
}
}
}
那这就完成没有发挥出HttpClient的一大优势(同一HttpClient可以发送多个请求),而以上代码一个请求就会创建一个HttpClient对象,同时我们调用HttpClient的Dispose()方法销毁它时,它就启动一个进程,关闭在它控制之下的套接字。也就是说,你下次请求连接时,必须重复整个连接新建过程。如果网络延迟很高,或者连接是受保护的(需要新一轮的SSL/TLS协商),就会非常痛苦。
所以HttpClient关闭套接字的过程并不快。当“关闭”套接字时,你真正做的是将TCP连接状态置为TIME_WAIT。在一个预先配置好的时间窗口内,Windows将保持该套接字的状态不变,默认情况下是4分钟。这是为了防止有任何剩余的数据包仍在传输。
所以以上代码是一个错误的使用方式,如果在一个高并发场景,服务器资源也会迅速耗尽。
所以我们的正确做法应该是:HttpClient应该只初始化一次,并在应用程序的整个生存期内重用。在负载很高的情况下,为每个请求初始化一个HttpClient类会耗尽可用的套接字数量。
以下展示正确的做法,就是对HttpClient做单例处理,使得应用生命周期内只有一个HttpClient对象,并可以重用,并且不调用Dispose()方法。
internal class HttpClientUtils
{
private static HttpClient _httpClient;
private readonly static object _lock = new object();
private readonly static HttpClientUtils _httpClientUtils = new HttpClientUtils(); static HttpClientUtils()
{
_httpClient = GetHttpClient();
} public static HttpClientUtils GetHttpClientUtils()
{
return _httpClientUtils;
} private HttpClientUtils()
{
} private static HttpClient GetHttpClient()
{
lock (_lock)
{
if (_httpClient == null)
{
_httpClient = new HttpClient();
//_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpClient.Timeout = TimeSpan.FromSeconds();
}
}
return _httpClient;
}
}
以上代码解决了HttpClient重用问题,但是竟然所有Http请求都使用同一HttpClient对象,那么新的问题也随之而来,可能我们会遇到不同的HttpClient请求需要带不同的Http请求头,
那么如下所示代码,就会造成很大问题,因为并不是所有的Http请求都需要此请求头。
client.DefaultRequestHeaders.Add("Accept", "application/json");
//....
那么问题的关键就在于,我们不能将请求头直接赋值到HttpClient对象的DefaultRequestHeaders属性上。
那么应该如何讲请求头和HttpClient对象分离。我们可以使用SendAsync(HttpRequestMessage request); ,在HttpRequestMessage中添加请求头。
以Get请求为例,示例如下:
public async Task<HttpResponseMessage> GetRequestAsync(string requestUri, string accessToken)
{
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, requestUri);
if (!string.IsNullOrEmpty(accessToken))
{
message.Headers.Add("Authorization", "Bearer " + accessToken);
}
try
{
var response = await _httpClient.SendAsync(message);
//var response = await this._client.GetAsync(requestUri);
if (response.IsSuccessStatusCode)
{
return response;
}
else
{
throw new Exception(response.Content.ReadAsStringAsync().Result);
}
}
catch (Exception ex)
{
throw ex;
}
}
可以看到我们将Http请求方法,Http请求Url以及请求头信息,封装到HttpRequestMessage对象中,使用 SendAsync(HttpRequestMessage request); 发送请求
而这正好解决高并发下Http请求发送的问题,避免不同Http请求互相造成干扰。
使用HttpClient实现并发请求的更多相关文章
- 对tomcat7模拟并发请求及相关配置参数的含义
这里的并不是真正的并发请求,因为for循环是间隔10毫秒,并且线程初始化也需要时间的,到真正执行http请求的时刻是不确定的. tomcat 的运行状态可以在webapps下的manage项目查看, ...
- httpclient的并发连接问题
昨天的搜索系统又出状况了,几个库同时重建索引变得死慢.经过一个上午的复现分析,确定问题出现httpclient的使用上(我使用的是3.1这个被广泛使用的遗留版本).搜索系统在重建索引时,是并发多个线程 ...
- 170314、工具:apache httpClient多线程并发情况下安全实用及工具类分享
简单用法介绍:介绍来源网络 建立连接:在HttpClient中使用多线程的一个主要原因是可以一次执行多个方法.在执行期间,每一个方法都使用一个HttpConnection实例.由于在同一时间多个连接只 ...
- 如何配置IIS处理多并发请求及存在的问题
很多时候多线程能快速高效独立的计算数据,应用比较多. 但今天遇到的多进程下的问题更是让人觉得复杂 多进程下static变量都要失效,就目前的平台和产品static使用是很多的,各种session.ca ...
- 查看 Apache并发请求数及其TCP连接状态
查看 Apache并发请求数及其TCP连接状态 (2011-06-27 15:08:36) 服务器上的一些统计数据: 1)统计80端口连接数 netstat -nat|grep -i "80 ...
- 查看 并发请求数及其TCP连接状态【转】
服务器上的一些统计数据: 1)统计80端口连接数netstat -nat|grep -i "80"|wc -l 2)统计httpd协议连接数ps -ef|grep httpd|wc ...
- HttpClientUtil [使用apache httpclient模拟http请求]
基于httpclient-4.5.2 模拟http请求 以get/post方式发送json请求,并获取服务器返回的json -------------------------------------- ...
- IIS处理并发请求时出现的问题及解决
一个ASP.NET项目在部署到生产环境时,当用户并发量达到200左右时,IIS出现了明显的请求排队现象,发送的请求都进入等待,无法及时响 应,系统基本处于不可用状态.因经验不足,花了很多时间精力解决这 ...
- Web大规模高并发请求和抢购的解决方案
电商的秒杀和抢购,对我们来说,都不是一个陌生的东西.然而,从技术的角度来说,这对于Web系统是一个巨大的考验.当一个Web系统,在一秒钟内收到数以万计甚至更多请求时,系统的优化和稳定至关重要.这次我们 ...
随机推荐
- jquery引入
网络地址:http://code.jquery.com/jquery-2.2.0.min.js 在需要的页面中直接使用网络地址,就不需要本地文件 <script type="text/ ...
- mpVue小程序全栈开发
1.微信小程序,mpVue和wepy的对比 2. 3.es6中关于数组的一些方法 <script> let arr = [,,,] // 遍历 arr.forEach(v => { ...
- 基于axios创建的实例使用axios.all,报错:this.$http is not a function,但请求成功
报以下错误: 原因: axios实例没有all这个方法,all是axios的静态方法 解决方法: 以下方法不是最好的,还没找到更好的解决办法,目前先这样解决.
- PHP实现的简单组词算法
?php //组词算法 function diyWords($arr,$m){ $result = array(); if ($m ==1){//只剩一个词时直接返回 return $arr; } i ...
- php单例模式的实例
class Config1 {} class Config { * 必须先声明一个静态私有属性:用来保存当前类的实例 * 1. 为什么必须是静态的?因为静态成员属于类,并被类所有实例所共享 * 2. ...
- python + slenium自动化测试设置元素等待
WebDriver 提供了两种类型的等待:显式等待和隐式等待. 显式等待 显式等待使 WebdDriver 等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常 (TimeoutExcepti ...
- python 全栈开发,Day50(Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏)
一.Javascript简介 Web前端有三层: HTML:从语义的角度,描述页面结构 CSS:从审美的角度,描述样式(美化页面) JavaScript:从交互的角度,描述行为(提升用户体验) Jav ...
- JQuery动画详解(四)
一:基本动画show()显示隐藏的匹配元素.这个就是 'show( speed, [callback] )' 无动画的版本.如果选择的元素是可见的,这个方法将不会改变任何东西.无论这个元素是通过hid ...
- jsp+servlet实现文件的上传和下载
实现文件的上传和下载首先需要理解几个知识,这样才可以很好的完成文件的上传和下载: (1):上传文件是上传到服务器上,而保存到数据库是文件名 (2):上传文件是以文件转换为二进制流的形式上传的 (3): ...
- UVa 562 - Dividing coins 均分钱币 【01背包】
题目链接:https://vjudge.net/contest/103424#problem/E 题目大意: 给你一堆硬币,让你分成两堆,分别给A,B两个人,求两人得到的最小差. 解题思路: 求解两人 ...