使用HttpClient的优解
新工作入职不久,目前仍然还在适应环境当中,笔者不得不说看别人的源码实在是令人痛苦。所幸前些日子终于将工作流畅地看了一遍,接下来就是熟悉框架技术的阶段了。
也正是在看源码的过程当中,有一个比较明显的用法细节引起了我的注意,我发现一位同事在请求远程Web Api时,虽然使用了 HttpClient 类,但是在用法上似乎有些欠考虑。代码抽象出来就是以下的模样:
using(var client = new HttpClient())
{
//do something
}
我们知道 using 关键字常常和实现了 IDisposable 接口的类型一起使用(如数据库连接和文件流操作),用于释放对象机资源(关于GC回收的相关知识可参考我的另一篇博文《CLR和.Net对象生存周期》),但是对于 HttpClient 这样直接和TCP/IP协议打交道的类型却是未必( HttpClient 继承了 HttpMessageInvoker 类, HttpMessageInvoker 实现了 IDisposable 接口,实现上是比较经典的代理模式),翻看一些国内外的文章都能看到对在 using 关键字中使用 HttpClient 的吐槽。事实是不是真的这样呢,其实只要做一个小实验就可以了。
让我们先试着运行以下代码
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10000; i++)
{
using (var client = new HttpClient())
{
var result = client.GetAsync("http://www.baidu.com").Result;
Console.WriteLine(result.StatusCode);
}
}
Console.ReadKey();
}
}
不出意外就会提示以下错误:
单纯为了解决问题而言,我们可以通过减小 HttpClient 的 Timeout 属性加快回收速度(修改系统变量可能会引发其他的问题),但实际上,这还是因为 HttpClient 消耗了太多套接字连接的关系。为了验证这个问题,我们可以使用TcpView这个小工具来查看下项目运行时的 TCP 连接数,如果你下载了代码运行后,会发现 TCP 连接和疯狗一样向上猛蹿。虽然还会有套接字回收的现象,但是和增加的速度相比确实是杯水车薪。
所以这时候我们需要换一种写法:
class Program
{
private static readonly HttpClient Client=new HttpClient();
static void Main(string[] args)
{
for (int i = 0; i < 10000; i++)
{
var result = Client.GetAsync("http://www.baidu.com").Result;
Console.WriteLine(result.StatusCode);
}
Console.ReadKey();
}
}
更换以上写法后,我们会发现无论我们将循环上限如何调整,也不会出现套接字连接资源不足的情况了,而TCPView的结果也好看得多,甚至如果我们每次都测试传输时间的话,我们会发现单次调用 HttpClient 而言,第二种代码比第一种代码要快得多。其实这很好理解,HttpClient内部维持一个专有的连接池,每个HttpClient实例的请求相互隔绝,加快速度的原因是因为重用了套接字,去除了套接字重新建立连接的过程。这也很好地解释了dudu园长的那一篇博客 《C#中HttpClient使用注意:预热与长连接》中的“预热”说法。盗一张图来说明一下套接字的使用情况。
因此,在使用 HttpClient 时我们知道以下几件小事
- 将其定义为单例模式(由单独的HttpClient维护连接池)
- 不要使用using关键字包裹(无效,套接字资源不会跟随释放)
- 尽量不要额外改变
HttpClient的一些特殊行为(如上文中的TimeOut) - 当你需要配置不同的Http请求时,允许生成并使用多个HttpClient
其实HttpClient还有一种使用隐患,DNS-Bug,这种做法国外也有同僚给出了相应的解释和解决方案,详情请见《Singleton HttpClient? Beware of this serious behaviour and how to fix it》
单例模式扩展开来也有很多的说法,根据C#的一些规范,在编程中我推荐三种做法
A. 静态构造器
这种方式适用于如上代码场景,使用静态构造器确保静态字段的实例化。
class Program
{
private static readonly HttpClient Client;
static Program()
{
Client=new HttpClient();
}
static void Main(string[] args)
{
//do something
}
}
B. HttpClientHelper
单例模式中,经典的双重检查锁定机制。
public static class HttpClientHelper
{
private static readonly object LockObj = new object();
private static HttpClient _client;
public static HttpClient HttpClient {
get
{
if (_client == null)
{
lock (LockObj)
{
if (_client == null)
{
_client= new HttpClient();
}
}
}
return _client;
}
}
}
C. HttpClientHelper
这是在编程规范中推荐的一种的做法,通过使用静态构造函数能够精确保证Client变量能够在它第一次被使用前被实例化。
当然你也可以直接使用内联的方式进行初始化,这样可以对类型进行性能优化,不过变量的初始化时间就无法进行精准控制了
public sealed class HttpClientHelper
{
private HttpClientHelper(){}
public static readonly HttpClient Client;
static HttpClientHelper()
{
Client=new HttpClient();
}
}
寄语
多点实践多点总结,为认识更深刻的代码世界而奋斗。
使用HttpClient的优解的更多相关文章
- hdu 2639 Bone Collector II (01背包,求第k优解)
这题和典型的01背包求最优解不同,是要求第k优解,所以,最直观的想法就是在01背包的基础上再增加一维表示第k大时的价值.具体思路见下面的参考链接,说的很详细 参考连接:http://laiba2004 ...
- HttpClient使用详解(转)
HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户 ...
- HDU 2639 (01背包第k优解)
/* 01背包第k优解问题 f[i][j][k] 前i个物品体积为j的第k优解 对于每次的ij状态 记下之前的两种状态 i-1 j-w[i] (选i) i-1 j (不选i) 分别k个 然后归并排序并 ...
- 01背包之求第K优解——Bone Collector II
http://acm.hdu.edu.cn/showproblem.php?pid=2639 题目大意是,往背包里赛骨头,求第K优解,在普通01背包的基础上,增加一维空间,那么F[i,v,k]可以理解 ...
- HttpClient使用详解
http://itindex.net/detail/52566-httpclient HttpClient使用详解 标签: httpclient | 发表时间:2015-01-22 12:07 | 作 ...
- hdu2639(背包求第k优解)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2639 题意:给出一行价值,一行体积,让你在v体积的范围内找出第k大的值 分析:dp[i][j][k]表 ...
- 关于01背包求第k优解
引用:http://szy961124.blog.163.com/blog/static/132346674201092775320970/ 求次优解.第K优解 对于求次优解.第K优解类的问题,如果相 ...
- Java进阶(三十二) HttpClient使用详解
Java进阶(三十二) HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们 ...
- (01背包 第k优解) Bone Collector II(hdu 2639)
http://acm.hdu.edu.cn/showproblem.php?pid=2639 Problem Description The title of this problem i ...
随机推荐
- 【翻译】MongoDB指南/聚合——聚合管道
[原文地址]https://docs.mongodb.com/manual/ 聚合 聚合操作处理数据记录并返回计算后的结果.聚合操作将多个文档分组,并能对已分组的数据执行一系列操作而返回单一结果.Mo ...
- HTML 事件(三) 事件流与事件委托
本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...
- Canvas绘图之平移translate、旋转rotate、缩放scale
画布操作介绍 画布绘图的环境通过translate(),scale(),rotate(), setTransform()和transform()来改变,它们会对画布的变换矩阵产生影响. 函数 方法 描 ...
- PHP好用但又容易忽略的小知识
1.PHP函数之判断函数是否存在 当我们创建了自定义函数,并且了解了可变函数的用法,为了确保程序调用的函数是存在的,经常会先使用function_exists判断一下函数是否存在.同样的method_ ...
- bzoj3095--数学题
题目大意:给定一个长度为n的整数序列x[i],确定一个二元组(b, k)使得S=Σ(k*i+b- x[i])^2(i∈[0,n-1])最小 看Claris大神的题解就行了.实际上就是用2次二次函数的性 ...
- Apache Cordova开发Android应用程序——番外篇
很多天之前就安装了visual studio community 2015,今天闲着么事想试一下Apache Cordova,用它来开发跨平台App.在这之前需要配置N多东西,这里找到了一篇MS官方文 ...
- ReactiveCocoa代码实践之-UI组件的RAC信号操作
上一节是自己对网络层的一些重构,本节是自己一些代码小实践做出的一些demo程序,基本涵盖大多数UI控件操作. 一.用UISlider实现调色板 假设我们现在做一个demo,上面有一个View用来展示颜 ...
- MySQL 优化之 ICP (index condition pushdown:索引条件下推)
ICP技术是在MySQL5.6中引入的一种索引优化技术.它能减少在使用 二级索引 过滤where条件时的回表次数 和 减少MySQL server层和引擎层的交互次数.在索引组织表中,使用二级索引进行 ...
- Prometheus 系统监控方案 一
最近一直在折腾时序类型的数据库,经过一段时间项目应用,觉得十分不错.而Prometheus又是刚刚推出不久的开源方案,中文资料较少,所以打算写一系列应用的实践过程分享一下. Prometheus 是什 ...
- linux的top命令参数详解
简介 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器. top显示系统当前的进程和其他状况,是一个动态显示过程,即可以通过用户按 ...