.NET下使用HTTP请求的正确姿势

一、前言

去年9月份的时候我看到过外国朋友关于.NET Framework下HttpClient缺陷的分析后对HttpClient有了一定的了解。前几日也有园友写了一篇关于HttpClient的分析文章, 于是我想深入探索一下在.NET下使用HTTP请求的正确姿势。姿势不是越多越好, 而在于精不精。如果不深入了解, 小朋友可能会这样想: 啊, 这个姿势不High, 那我换一个吧, 殊不知那一个姿势也有问题啊, 亲。

中文版: https://oschina.net/news/77036/httpclient

英文版: https://www.infoq.com/news/2016/09/HttpClient

张大大版: http://www.cnblogs.com/lori/p/7692152.html

 

二、准备好床和各种姿势

1. 研究姿势必然是要先准备好支撑点, 作为一个传统的人, 还是比较喜欢床。

.NET Framework, .NET CORE Windows, .NET CORE Linux, .NET CORE Mac

2. 姿势有以下几种, 如果小朋友们有各特别的可以告诉我呀, 我很乐于尝试的。

HttpClient, WebClient, HttpWebRequest

 

三、让我们大干一场吧

Windows下统计端口使用的命令: netstat -ano | find "{port}" /c 

Linux 下统计端口使用的命令:  netstat -nat|grep -i "{port}"|wc -l

 

HttpWebRequest 测试代码如下

    class Program{        static void Main(string[] args){Parallel.For(0, 10, (i) =>{                while (true){                    var webRequest = (HttpWebRequest)WebRequest.CreateHttp("http://");                    var response = webRequest.GetResponse();response.Dispose();Console.WriteLine($"Process: {i}.");Thread.Sleep(5);}});Console.Read();}}



.NET Framework   .NET Core Windows  .NET Core Linux.NET Core Mac
HttpWebRequest 2 迅速攀升到1000+ 性能很差, 攀升到70+并稳定

 

WebClient因为有IDisposable接口, 于是我做两份测试


        static void Main(string[] args){Parallel.For(0, 10, (i) =>{                while (true){                    using (WebClient client = new WebClient()){client.DownloadString("http://");Console.WriteLine($"Process: {i}.");}Thread.Sleep(5);}});Console.Read();}



.NET Framework   .NET Core Windows  .NET Core Linux.NET Core Mac
WebClient 2 迅速攀升到1000+ 性能较差, 攀升到400+稳定


        static void Main(string[] args){Parallel.For(0, 10, (i) =>{WebClient client = new WebClient();                while (true){client.DownloadString("http://");Console.WriteLine($"Process: {i}.");Thread.Sleep(5);}});Console.Read();}



 .NET Framework   .NET Core Windows  .NET Core Linux  .NET Core Mac
 WebClient  2  迅速攀升到1000+  迅速攀升到1000+

 

HttpClient有IDisposable接口, 也做两份测试


        static void Main(string[] args){Parallel.For(0, 10, (i) =>{HttpClient client = new HttpClient();                while (true){                 
   
var html = client.GetStringAsync("http://").Result;Console.WriteLine($"Process: {i}.");Thread.Sleep(5);}});Console.Read();}



.NET Framework.NET Core Windows.NET Core Linux.NET Core Mac
HttpClient101010

 

        static void Main(string[] args){Parallel.For(0, 10, (i) =>{              
while (true){                  
 
using (HttpClient client = new HttpClient()){                    
   
var html = client.GetStringAsync("http://").Result;Console.WriteLine($"Process: {i}.");}Thread.Sleep(5);}});Console.Read();}



.NET Framework.NET Core Windows.NET Core Linux.NET Core Mac
HttpClient迅速攀升到1000+迅速攀升到1000+性能较差, 攀升到200+

  

结论


.NET Framework.NET Core Windows.NET Core Linux.NET Core Mac
HttpWebRequestOKAbnormalAbnormal
WebClientOKAbnormalAbnormal
HttpClient(每个线程一个对象)OK OKOK
HttpClient(using)AbnormalAbnormalAbnormal

  

有意思的细节与疑问

1. WebClient和HttpWebRequest为什么在10个线程下端口数为2并且都为2

2. Linux下并行性能明显变差

 

四、追根溯源

下载.net45源码和corefx源码

http://referencesource.microsoft.com/ 右上角Download

https://github.com/dotnet/corefx

 

1. 分析.NET Core下WebClient的代码, 发现它是使用WebRequest即HttpWebRequest来请求数据

 

2. 分析.NET Core下HttpWebRequest的代码找到SendRequest方法

熟悉吗?!!原来.NET Core一切的根源都出在HttpClient身上...

 

3. 顺着HttpClient代码我们可以发现, 微软为Windows, Unix各自实现了WinHttpHandler和CurlHandler, 猜测Uniux下使用的是Curl. 最终确实能查到Windows下是DLLImport了winhttp.dll, 但Unix系统是DLLImport的 System.Net.Http.Native, 这是个什么我暂时不清楚, 也不清楚它跟curl的关系, 也许是一个中转调用。

 

4. 我们再回过头来看.NET Framework下为什么HttpWebRequest和WebClient是正常的, WebClient依然是使用的HttpWebRequest, 因此推断.NET Framework的HttpWebRequest的实现与.NET Core是不一致的。简单的查找代码, 果然每一个Http请求是由ServicePointManager管理的ServicePoint来实现的, 并且ServicePoint是使用.NET下Socket来实现的, 一切就明了了。现在对刚才说的 “WebClient和HttpWebRequest为什么在10个线程下端口数为2并且都为2”有感觉了吧?我们把刚才的测试代码再加上一行

        static void Main(string[] args){ServicePointManager.DefaultConnectionLimit = 10;Parallel.For(0, 10, (i) =>{                while (true){                    var webRequest = (HttpWebRequest)WebRequest.CreateHttp("http://www.ooodata.com:5000");                    var response = webRequest.GetResponse();response.Dispose();Console.WriteLine($"Process: {i}.");Thread.Sleep(5);}});Console.Read();}



.NET Framework.NET Core Windows.NET core Linux.NET Core Mac
HttpWebRequest10  迅速攀升到1000+性能很差, 攀升到70+并稳定

 

 

大家看.NET Core下虽然可以设置 ServicePointManager.DefaultConnectionLimit = 10; 但是依然没什么卵用...  原因也很明显, HttpWebRequest根本没有使用ServicePointManager做管理。在我查了源码后虽然.NET Core实现了ServicePointManager和ServicePoint, 不过已经迁到另外一个项目下面, 也未发现有什么作用。所以大家千万要注意,不要以为在.NET Core可以设置ServicePointManager.DefaultConnectionLimit这个值了, 就以为.NET Framework下的效果会一致( 其它地方同理)

 

5. HttpClient在.NET Framework下的代码我没有找到, ILSpy也查看不了, 但猜想应该是和.NET Core下一致的, 所以才会有一样的表象, 有大神知道的可以告诉我一下。

 

五、好累啊, 终于交差了, 就是不知道满足不满足

1. 在.NET Framework下尽量使用HttpWebRequest或者WebClient, 并且根据你自己的多线程情况设置 ServicePointManager.DefaultConnectionLimit的值, 以及ThreadPool.SetMinThreads(200, 200)的值

2. 在.NET Framework下如果一定要使用HttpClient, 一个线程使用一个HttpClient对象, 这样不会出现端口被耗尽的情况

3. 在.NET Core 2.0下只有HttpClient一条路选, 一个线程使用一个HttpClient对象, 当然也许我们可以参照.NET Framework下的代码重新实现一个ServicePointManager管理的HttpWebRequest, 这是后话了

 

六、抽一根烟吧

1. 大胆猜想一下, 微软应该是赶进度才偷懒使用HttpClient来实现HttpWebRequest导致的吧。

2. Linux并行性能好像差很多, 原因不明, 请听下回分解

3. 这也就是开源的魅力所在了吧! 我们可以顺藤摸瓜, 查明真相。让我们一起为.NET Core的开源事业奉献自己的一份力吧(其实我只是不想丢饭碗好吧:::)

4. 如果有说错请指正, 不接受漫骂

5. 欢迎各路大神和作品加入 https://github.com/dotnetcore (中国 .net core 开源小分队)

6. 月收入低于3万的也是程序员!!!!

原文地址:http://www.cnblogs.com/modestmt/p/7724821.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323159.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jzoj5123-diyiti【统计,容斥】

正题 题目大意 n个木棒,求用6个木棒组成正方形的方案总数。 解题思路 这个正方形边有的木棒数两种可能3,1,1,13,1,1,13,1,1,1和2,2,1,12,2,1,12,2,1,1。 第一种可以枚举111,然后用fxf_xfx​表示两根木棒和为x的方案数,用fxf_xfx​加容斥可…

【LSB】图片隐写主体函数

关于图像隐写 图像隐写是一种有效的方式来交换隐藏的消息,而不会引起怀疑。它的工作原理是用lbs算法将消息编码为图像像素的颜色值。 这种功能基于浏览器的最新特性比如File API和Canvas,如果你的浏览器不支持该该功能,请下载最新的浏览器。…

C++描述杭电OJ 2012. 素数判定 ||

C描述杭电OJ 2012. 素数判定 || Problem Description 对于表达式n^2n41&#xff0c;当n在&#xff08;x,y&#xff09;范围内取整数值时&#xff08;包括x,y&#xff09;(-39<x<y<50)&#xff0c;判定该表达式的值是否都为素数。 Input 输入数据有多组&#xff0c;…

欢乐纪中某B组赛【2018.12.8】

前言 题目有毒系列。反正我觉得第1题最难&#xff0c;3,4题简单些。 跟jz大佬做题 成绩 RankRankRank是有算别人的 RankRankRankPersonPersonPersonScoreScoreScoreAAABBBCCCDDD111meselfmeselfmeself280280280000808080100100100100100100121212xjqxjqxjq1001001000000001001…

【Ajax】创建并封装

创建 <!DOCTYPE html> <html> <head><title></title><style>#button1{background:skyblue;border-radius:20px;width:100px;}</style> </head> <body> <button id"button1">按钮</button> <i…

C++描述杭电OJ 2016. 数据的交换输出 ||

C描述杭电OJ 2016. 数据的交换输出 || Problem Description 输入n(n<100)个数&#xff0c;找出其中最小的数&#xff0c;将它与最前面的数交换后输出这些数。 Input 输入数据有多组&#xff0c;每组占一行&#xff0c;每行的开始是一个整数n&#xff0c;表示这个测试实例…

ABP从入门到精通(2):aspnet-zero-core 使用MySql数据库

关于 asp.net zero core 项目的启动及说明&#xff0c;请观看我前面的博文ABP从入门到精通&#xff08;1&#xff09;&#xff1a;aspnet-zero-core项目启动及各项目源码说明 本操作对于ABP默认项目应该也是适用的&#xff01; 一.移除默认的SqlServer相关程序包 需要移除“MyC…

P3629-[APIO2010]巡逻【树的直径】

正题 题目大意 刚开始一棵树&#xff0c;在树中加入k条边(k<2)使得这些边都得走过的情况下&#xff0c;每个点都到达并回到原点的最少边。 解题思路 首先我们发现如果不加边的话答案是2∗(n−1)2*(n-1)2∗(n−1)。 之后我们考虑k1k1k1的情况&#xff0c;我们找树的直径&am…

15个顶级Java多线程面试题及答案

转载自 15个顶级Java多线程面试题及答案 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得更多职位&#xff0c;那么你应该准备很多关于多线程的问题。 他们会问面试者很多令人混淆的Java线程问题。面试官只是想确信面试者有足够的Java线程与并…

Visual Studio 15.5预览版先睹为快

Microsoft延续了Visual Studio 2017快速迭代开发的步伐&#xff0c;最新发布了15.5预览版&#xff0c;这是VS2017这一广受欢迎的IDE自发布以来的第五次更新&#xff0c;该预览版的发布使用户可以先睹为快。 该预览版启用了一个称为“Stepping Back”的调试历史新特性。IntelliT…

C++描述杭电OJ 2017.字符串统计||

C描述杭电OJ 2017.字符串统计|| Problem Description 对于给定的一个字符串&#xff0c;统计其中数字字符出现的次数。 Input 输入数据有多行&#xff0c;第一行是一个整数n&#xff0c;表示测试实例的个数&#xff0c;后面跟着n行&#xff0c;每行包括一个由字母和数字组成…

【Android】实现页面跳转

对比html&#xff0c;安卓的页面跳转要难的多。 html只需要一个a标签即可实现页面的跳转&#xff0c;而安卓要分三步走 第一步 在activity_main.xml创建一个按钮 <Buttonandroid:id"id/btn1"android:layout_width"match_parent"android:layout_heigh…

jzoj100046-收集卡片【暴力】

正题 题目大意 给一个字符串&#xff0c;求一个最短的子串包含所有在这个字符串里出现的字母。 解题思路 枚举右指针&#xff0c;左指针移动到刚好满足要求的位置。每次就一个最小值。 时间复杂度:O(56n)O(56n)O(56n) code #include<cstdio> #include<algorithm>…

C++描述杭电OJ 2019. 数列有序 ||

C描述杭电OJ 2019. 数列有序 || Problem Description 有n(n<100)个整数&#xff0c;已经按照从小到大顺序排列好&#xff0c;现在另外给一个整数x&#xff0c;请将该数插入到序列中&#xff0c;并使新的序列仍然有序。 Input 输入数据包含多个测试实例&#xff0c;每组数…

【Android布局】控件布置

居中对齐 在相对布局中简单点 RelativeLayout 水平居中&#xff1a;android:layout_centerHorizontal“true” 垂直居中&#xff1a;android:layout_centerVertical“true” 水平垂直居中&#xff1a;android:layout_centerInParent“true” 在线性布局中 LinearLayout 首先必…

jzoj100047-基因变异【位运算,bfs】

正题 题目大意 一个长度为nnn的序列aaa。 对于一个数每秒可以将一个二进制位取反或异或aaa中的一个数。 qqq个询问&#xff0c;询问从xxx变化到yyy最少要多少秒。 解题思路 对于一个x和yx和yx和y&#xff0c;设 xxorwyx\ xor\ wyx xor wy ⇒xyxorw\Rightarrow xy\ xor\ w⇒xy…

写一个算法统计在输入字符串中各个字符出现的频度

#include<bits/stdc.h> using namespace std;void fun() {int a[62]{0} ;char c;cout<<"请输入一个字符串&#xff0c;以#结尾:";cin>>c;while(c!#){if(c>A&&c<Z) a[c-A];else if(c>a&&c<z) a[c-a26];else a[c-052];c…

ABP从入门到精通(3):aspnet-zero-core 使用Redis缓存

一.Redis是什么&#xff1f; redis是一个key-value存储系统。和Memcached类似&#xff0c;它支持存储的value类型相对更多&#xff0c;包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash&#xff08;哈希类型&#xff09;。这些数据类型都支持pus…

【Mysql】win10上Mysq的l安装

最近想学习java的jdbc&#xff0c;完成注册登录系统&#xff0c;有了php的经验&#xff0c;&#xff0c;就简单多。但是php是基于wamp集成环境的&#xff0c;当时就是由于win10安装mysql老是错误&#xff0c;所以选择了集成环境&#xff0c;这对入门来讲降低了很大的难度。这次…

jzoj100044-abcd【多重背包,二进制压缩,dp】

正题 题目大意 给出长度为nnn的序列a,b,c,da,b,c,da,b,c,d 求一个序列eee满足 (∑i1nei∗ci)0(e∈[ai..bi])(\sum _{i1}^ne_i*c_i)0(e\in [a_i..b_i])(i1∑n​ei​∗ci​)0(e∈[ai​..bi​]) 求 max{∑i1nei∗di}max\{\sum_{i1}^ne_i*d_i\}max{i1∑n​ei​∗di​} 解题思路 我…