.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,一经查实,立即删除!

相关文章

【LSB】图片隐写主体函数

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

【Ajax】创建并封装

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

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

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

Visual Studio 15.5预览版先睹为快

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

【Android】实现页面跳转

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

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;这对入门来讲降低了很大的难度。这次…

Entity Framework Core 2.0 使用代码进行自动迁移

一.前言 我们在使用EF进行开发的时候&#xff0c;肯定会遇到将迁移更新到生产数据库这个问题&#xff0c;前面写了一篇文章介绍了Entity Framework Core 2.0的入门使用&#xff0c;这里面介绍了使用命令生成迁移所需的SQL&#xff0c;然后更新到生产数据库的方法。这里还有另一…

【JDBC】各版本jar包下载网址及Tomcat下载

http://central.maven.org/maven2/mysql/mysql-connector-java/ 自己的mysql的版本号会在登录之后显示 Tomcat链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bOMY_6hp7nV5KpU496YPlA 提取码&#xff1a;rerg

Mybatis中强大的功能元素:resultMap

转载自 Mybatis中强大的功能元素&#xff1a;resultMap 前言 在Mybatis中&#xff0c;有一个强大的功能元素resultMap。当我们希望将JDBC ResultSets中的数据&#xff0c;转化为合理的Java对象时&#xff0c;你就能感受到它的非凡之处。正如其官方所述的那样&#xff1a; re…

将编号为0和1的两个栈存放于一个数组空间V[m]中。

目录 1.题目描述 2.算法实现 1.题目描述 将编号为0和1的两个栈存放于一个数组空间V[m]中,栈底分别处于数组的两端。当第0号栈的栈顶指针top[0]等于-1时该栈为空&#xff1b;当第1号栈的栈顶指针top[1]等于m时该栈为空。两个栈均从两端向中间增长&#xff08;见图&#xff09;…

ASP.NET Core 2.0 依赖注入

问题 如何使用 ASP.NET Core 服务容器进行依赖注入&#xff1f; 答案 创建一个服务 public interface IGreetingService { string Greet(string to); } public class GreetingService : IGreetingService { public string Greet(string to){ return $"H…

面试 - 要不简单聊一下你对MySQL索引的理解?

转载自 面试 &#xff0d; 要不简单聊一下你对MySQL索引的理解&#xff1f; MySQL索引&#xff1f;这玩意儿还能简单聊&#xff1f;明显是在挖坑&#xff0c;幸好老夫早有准备&#xff0c;切听我一一道来。 一、索引是什么? 索引是帮助MySQL高效获取数据的数据结构。 二、…

ABP从入门到精通(4):使用基于JWT标准的Token访问WebApi

项目&#xff1a;asp.net zero 4.2.0 .net core&#xff08;1.1&#xff09; 版本 我们做项目的时候可能会遇到需要提供api给app调用&#xff0c;ABP动态生成的WebApi提供了方便的基于JWT标准的Token访问方式供我们访问API&#xff0c;不用在代码上做任何改动&#xff0c;很方便…

ASP.NET Core 2.0 + EF6 + Linux +MySql混搭

好消息&#xff01;特好消息&#xff01;同时使用ASP.NET Core 2.0和.NET Framework类库还能运行在linux上的方法来啦&#xff01; 是的&#xff0c;你没有看错&#xff01;ASP.NET Core 2.0&#xff0c;.NET Framework类库&#xff0c;linux通通都给你&#xff0c;不要998只要…

面试被问Mysql没答上来?阿里P5:总结了55道常见面试题,收藏一波

转载自 面试被问Mysql没答上来&#xff1f;阿里P5:总结了55道常见面试题&#xff0c;收藏一波 正文开始前&#xff0c;分享阿里 P8 高级架构师吐血总结的 《Java 核心知识体系&面试资料.pdf》, 非卖课程的哟&#xff01; 阿里 P8 级高级架构师吐血总结的一份 Java 核心知…

开源纯C#工控网关+组态软件(四)上下位机通讯原理

一、 网关的功能&#xff1a;承上启下 最近有点忙&#xff0c;更新慢了。感谢园友们给予的支持&#xff0c;现在github上已经有。目标是最好的开源组态&#xff0c;看来又近一步^^ 之前有提到网关是物联网的关键环节&#xff0c;它的作用就是承上启下。 下位机有下位机的语言…

微软Tech Summit 2017,等你来打Call

2017年10月31至11月3日&#xff0c;由微软举办的Tech Summit 2017技术暨生态大会将在北京盛大举办&#xff0c;要在北京连开四天。今年的技术大会看头十足&#xff0c;不仅有大咖级人物带来十二大主题课程&#xff0c;更有三天四场的主题之夜。微软技术大会最早是由微软技术教育…

ABP从入门到精通(5):.扩展国际化语言资源

ABP的有些组件使用的该组件自带的语言包资源&#xff0c;所以在有些时候会因为我们当前使用的语言对应的语言包不全&#xff0c;而造成日志一直记录WARN。ABP给我们提供了扩展语言包资源的接口&#xff0c;可以解决这个问题。 以下示例代码适用于ABP .net core版本。我要为名为…

CoreCLR源码探索(八) JIT的工作原理(详解篇)

在上一篇 我们对CoreCLR中的JIT有了一个基础的了解,这一篇我们将更详细分析JIT的实现. JIT的实现代码主要在https://github.com/dotnet/coreclr/tree/master/src/jit下, 要对一个的函数的JIT过程进行详细分析, 最好的办法是查看JitDump. 查看JitDump需要自己编译一个Debug版本的…