如果找的是core的HttpClientFactory 出门右转。

  1. 官方写法,高并发下,TCP连接不能快速释放,导致端口占完,无法连接

    Dispose 不是马上关闭tcp连接

    主动关闭的一方为什么不能马上close而是进入timewait状态:TCP四次挥手客户端关闭链接为什么要等待2倍MSL

    正确写法一个域(一个地址) 保证一个静态httpclient操作,保证重用tcp连接。

  2. 如果HttpClient唯一,如果请求头内容需要变化怎么办,异常:"集合已修改;可能无法执行枚举操作"

    HttpClient有个接口SendAsync。看源码知道其实HttpClient内部get,post,put,delete最终都是调用SendAsync。

    这个方法可以允许用户传递HttpRequestMessage,内部包含(HttpRequestHeaders)

  3. HttpClient 死锁

    关于Task同步上下文 造成死锁问题就不多解释。避免方法就是ConfigureAwait(false)或者await always。最好是await always。传送门

    说下不用await 而使用类似HttpClient.GetStringAsync(uri).Result 直接同步获取为什么没有死锁

    因为HttpClient源码里面用到async await的地方几乎都加了ConfigureAwait(false)。233333

  4. 预热和长连接

    其实这是嘴巴dudu园长大人在很久以前就做分析过 传送门

  5. HttpClient 利用stream和json.net 减少内存开销,加快反序列化过程

    我们一般的请求流程:发起请求,获取返回的string对象,然后反序列化成我们想要的对象。而其实可以利用stream直接反序列化成我们想要的对象。

    而且可以是在HttpCompletionOption.ResponseHeadersRead的情况下。传送门

    封印:

    1. /// <summary>
    2. /// 异步http请求者。
    3. /// 注意:杜绝new的形式,可以IOC容器单例注入
    4. /// 如果多个address地址,通过name key形式注入多个单例
    5. /// </summary>
    6. public class HttpAsyncSender
    7. {
    8. private readonly ILogger _logger = LoggerManager.GetLogger(typeof(HttpAsyncSender));
    9. //静态 重用tcp连接 长连接(not dispose)https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
    10. private readonly HttpClient _httpClient = new HttpClient();
    11.  
    12. public HttpAsyncSender(Uri baseUri, int timeoutSeconds = , bool keepAlive = true, bool preheating = false, long maxResponseContentBufferSize = )
    13. {
    14. //基础地址
    15. _httpClient.BaseAddress = baseUri;
    16. //超时
    17. if (timeoutSeconds != )
    18. {
    19. _httpClient.Timeout = TimeSpan.FromSeconds(timeoutSeconds);
    20. }
    21. //response最大接收字节 默认2gbmstsc
    22. if (maxResponseContentBufferSize != )
    23. {
    24. _httpClient.MaxResponseContentBufferSize = maxResponseContentBufferSize;
    25. }
    26. //长连接 //https://www.cnblogs.com/lori/p/7692152.html http 1.1 default set keep alive
    27. if (keepAlive)
    28. {
    29. _httpClient.DefaultRequestHeaders.Connection.Add("keep-alive");
    30. }
    31. //httpclient 预热 the first request //https://www.cnblogs.com/dudu/p/csharp-httpclient-attention.html
    32. if (preheating)
    33. {
    34. _httpClient.SendAsync(new HttpRequestMessage
    35. {
    36. Method = new HttpMethod("HEAD"),
    37. RequestUri = new Uri(baseUri + "/")
    38. }).Result.EnsureSuccessStatusCode();
    39. }
    40. }
    41.  
    42. #region 异步
    43.  
    44. /// <summary>
    45. /// GetAsync 注意 await always
    46. /// </summary>
    47. /// <param name="url"></param>
    48. /// <param name="cancellationToken"></param>
    49. /// <param name="jsonSerializerSettings"></param>
    50. /// <returns></returns>
    51. public async Task<T> GetAsync<T>(string url, CancellationToken cancellationToken, JsonSerializerSettings jsonSerializerSettings = null)
    52. {
    53. try
    54. {
    55. //https://johnthiriet.com/efficient-api-calls/ 减少内存开销 利用stream特性 加快反序列化
    56. using (var request = new HttpRequestMessage(HttpMethod.Get, url))
    57. {
    58. var res = await SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
    59. var resStesam = await res.Content.ReadAsStreamAsync().ConfigureAwait(false);
    60. if (res.IsSuccessStatusCode)
    61. {
    62. return DeserializeJsonFromStream<T>(resStesam, jsonSerializerSettings);
    63. }
    64. var resStr = await StreamToStringAsync(resStesam).ConfigureAwait(false);
    65. _logger.Error($"HttpAsyncSender, GetAsync ,response fail StatusCode:{res.StatusCode} resStr:{resStr} BaseAddress:{_httpClient.BaseAddress},Url:{url}");
    66. }
    67. }
    68. catch (AggregateException ae)
    69. {
    70. _logger.Error($"HttpAsyncSender,GetAsync AggregateException,BaseAddress:{_httpClient.BaseAddress},Url:{url} ae:{ae.Flatten()}");
    71. throw;
    72.  
    73. }
    74. catch (Exception e)
    75. {
    76. _logger.Error($"HttpAsyncSender,GetAsync Exception,BaseAddress:{_httpClient.BaseAddress},Url:{url}",e);
    77. throw;
    78. }
    79.  
    80. return default(T);
    81. }
    82.  
    83. /// <summary>
    84. /// PostAsync 注意 await always
    85. /// </summary>
    86. /// <param name="url"></param>
    87. /// <param name="content"></param>
    88. /// <param name="cancellationToken"></param>
    89. /// <param name="jsonSerializerSettings"></param>
    90. /// <returns></returns>
    91. public async Task<TRes> PostAsync<TReq, TRes>(string url, TReq content, CancellationToken cancellationToken, JsonSerializerSettings jsonSerializerSettings = null)
    92. {
    93. try
    94. {
    95. //https://johnthiriet.com/efficient-api-calls/ 减少内存开销 利用stream特性 加快反序列化
    96. using (var request = new HttpRequestMessage(HttpMethod.Post, url))
    97. using (var httpContent = CreateHttpContent(content, jsonSerializerSettings))
    98. {
    99. request.Content = httpContent;
    100. var res = await SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
    101. var resStesam = await res.Content.ReadAsStreamAsync().ConfigureAwait(false);
    102. if (res.IsSuccessStatusCode)
    103. {
    104. return DeserializeJsonFromStream<TRes>(resStesam, jsonSerializerSettings);
    105. }
    106. var resStr = await StreamToStringAsync(resStesam).ConfigureAwait(false);
    107. _logger.Error($"HttpAsyncSender, PostAsync ,response fail StatusCode:{res.StatusCode} resStr:{resStr} BaseAddress:{_httpClient.BaseAddress},Url:{url}");
    108. }
    109. }
    110. catch (AggregateException ae)
    111. {
    112. _logger.Error($"HttpAsyncSender,PostAsync AggregateException,BaseAddress:{_httpClient.BaseAddress},Url:{url} ae:{ae.Flatten()}");
    113. throw;
    114.  
    115. }
    116. catch (Exception e)
    117. {
    118. _logger.Error($"HttpAsyncSender,PostAsync Exception,BaseAddress:{_httpClient.BaseAddress},Url:{url}",e);
    119. throw;
    120. }
    121.  
    122. return default(TRes);
    123. }
    124.  
    125. /// <summary>
    126. /// SendAsync 注意 await always 当需要动态改变request head的时候 调用此方法。 解决 "集合已修改;可能无法执行枚举操作"
    127. /// </summary>
    128. /// <param name="httpRequestMessage"></param>
    129. /// <param name="completionOption"></param>
    130. /// <param name="cancellationToken"></param>
    131. /// <returns></returns>
    132. public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequestMessage, HttpCompletionOption completionOption, CancellationToken cancellationToken)
    133. {
    134. try
    135. {
    136. return await _httpClient.SendAsync(httpRequestMessage, completionOption, cancellationToken).ConfigureAwait(false);
    137. }
    138. catch (AggregateException ae)
    139. {
    140. _logger.Error(
    141. $"HttpAsyncSender,SendAsync AggregateException,BaseAddress:{_httpClient.BaseAddress},Url:{httpRequestMessage.RequestUri} ae:{ae.Flatten()}");
    142. throw;
    143.  
    144. }
    145. catch (Exception e)
    146. {
    147. _logger.Error($"HttpAsyncSender,SendAsync Exception,BaseAddress:{_httpClient.BaseAddress},Url:{httpRequestMessage.RequestUri}",e);
    148. throw;
    149. }
    150. }
    151.  
    152. #endregion
    153.  
    154. private T DeserializeJsonFromStream<T>(Stream stream, JsonSerializerSettings jsonSerializerSettings = null)
    155. {
    156. if (stream == null || stream.CanRead == false)
    157. return default(T);
    158.  
    159. using (var sr = new StreamReader(stream))
    160. using (var jtr = new JsonTextReader(sr))
    161. {
    162. var js = jsonSerializerSettings == null ? new JsonSerializer() : JsonSerializer.Create(jsonSerializerSettings);
    163. var searchResult = js.Deserialize<T>(jtr);
    164. return searchResult;
    165. }
    166. }
    167.  
    168. private async Task<string> StreamToStringAsync(Stream stream)
    169. {
    170. string content = null;
    171.  
    172. if (stream != null)
    173. using (var sr = new StreamReader(stream))
    174. content = await sr.ReadToEndAsync();
    175.  
    176. return content;
    177. }
    178.  
    179. public void SerializeJsonIntoStream(object value, Stream stream, JsonSerializerSettings jsonSerializerSettings = null)
    180. {
    181. using (var sw = new StreamWriter(stream, new UTF8Encoding(false), , true))
    182. using (var jtw = new JsonTextWriter(sw) { Formatting = Formatting.None })
    183. {
    184. var js = jsonSerializerSettings==null?new JsonSerializer():JsonSerializer.Create(jsonSerializerSettings);
    185. js.Serialize(jtw, value);
    186. jtw.Flush();
    187. }
    188. }
    189.  
    190. private HttpContent CreateHttpContent<T>(T content, JsonSerializerSettings jsonSerializerSettings = null)
    191. {
    192. HttpContent httpContent = null;
    193. if (content != null)
    194. {
    195. var ms = new MemoryStream();
    196. SerializeJsonIntoStream(content, ms, jsonSerializerSettings);
    197. ms.Seek(, SeekOrigin.Begin);
    198. httpContent = new StreamContent(ms);
    199. httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    200. }
    201.  
    202. return httpContent;
    203. }
    204. }

9102年了,汇总下HttpClient问题,封印一个的更多相关文章

  1. Cocos开发中Visual Studio下HttpClient开发环境设置

    Cocos2d-x 3.x将与网络通信相关的类集成到libNetwork类库工程中,这其中包括了HttpClient类.我们需要在Visual Studio解决方案中添加libNetwork类库工程. ...

  2. .Net45下HttpClient的几个缺陷

    前言 最近在写WebClientApi这个组件,底层使用HttpClient,发现HttpClient有许多低级的错误,使用者一不小心就可能会正常的去调用它的这些错误,得不到预期的结果.本文我把我认为 ...

  3. Swift技术之如何在iOS 8下使用Swift设计一个自定义的输入法 (主要是NSLayoutConstraint 的使用)

    当前位置: > Swift新手入门 > Swift技术之如何在iOS 8下使用Swift设计一个自定义的输入法 时间:2014-09-10 16:49来源:未知 作者:啊成 举报 点击:5 ...

  4. Eclipse 下如何引用另一个项目的Java文件

    有关联的2个项目,有些类是相同的.例如实体类. 如果你采用 Ctrl + C & Ctrl + V 的方式,以后再有改动,2个项目就都需要改动. 怎样才能只改动一个呢? 答案就是,在一个项目( ...

  5. Eclipse 下如何引用另一个项目的资源文件

    为什么要这么做?可参考:Eclipse 下如何引用另一个项目的Java文件 下面直接说下步骤:(项目A 引用 项目B的资源文件) 1.右键 项目A,点击菜单 Properties 2.在弹出的框中,点 ...

  6. Qt ------ window下工程项目打包成一个exe程序

    最近,在学习QT5的过程中,想尝试着把自己写的工程程序给打包发布出来,在任何一台windows系统都能运行,这样就不会限于电脑需不需要安装QT安装包了. 首先,先介绍自己使用的环境.我使用的QT版本是 ...

  7. 在Linux下,如何分析一个程序达到性能瓶颈的原因

    0.在Linux下,如何分析一个程序达到性能瓶颈的原因,请分别从CPU.内存.IO.网络的角度判断是谁导致的瓶颈?注意现在的机器CPU是多核 1.用sar -n DEV 1 10 2.用iotop命令 ...

  8. 【小程序】微信小程序打开其他小程序(打开同一主体公众号下关联的另一个小程序)

    微信小程序打开其他小程序(打开同一公众号下关联的另一个小程序) 注:只有同一(主体)公众号下的关联的小程序之间才可相互跳转  wx.navigateToMiniProgram(OBJECT) wx.n ...

  9. PyCharm在同一个包(package)下,如何把一个.py文件导入另外一个.py文件下

    PyCharm在同一个包(package)下,如何把一个.py文件导入另外一个.py文件下 在同一个包下只需要用import 掉以后就可以找到模块所在的位置,但是如果不在同一个包下,在需要返回父级调用 ...

随机推荐

  1. [二分答案][NOIP2015]跳石头

    跳石头 题目描述 一年一度的“跳石头”比赛又要开始了!这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间,有 N 块岩石(不含起 ...

  2. get通配符

    这篇文章是在上一篇常用正则表达式(合) https://blog.csdn.net/yeyeye200/article/details/86186889 基础上的延伸:关于通配符的使用~ 一开始get ...

  3. idea debug快捷键 快速查找类

    快速查找类或者文件比如xml .txt Ctrl + Shift + N 快速查找类 双击Shift 选中代码右移 Tab 选中代码左移 Shift + Tab 选中代码上下移 Shift + Alt ...

  4. 【机器学习】主成分分析法 PCA (II)

    主成分分析法(PAC)的优化——选择主成分的数量 根据上一讲,我们知道协方差为① 而训练集的方差为②. 我们希望在方差尽可能小的情况下选择尽可能小的K值. 也就是说我们需要找到k值使得①/②的值尽可能 ...

  5. VMware Workstation:安装windows xp系统

    https://blog.csdn.net/nicergj/article/details/83651603

  6. uiautomatorviewer报错“Error taking device screenshot: EOF” ,

    uiautomatorviewer报错“Error taking device screenshot: EOF”  ,千万不要装手机助手,不要装手机助手,不要装手机助手 uiautomatorview ...

  7. Fiddler抓包工具安装与使用

    1.Fiddler简介2.Fiddler安装步骤3.Fiddler目录结构4.Fiddler证书配置5.Fiddler录制配置6.Fiddler工作原理7.Fiddler界面详解 1.Fiddler简 ...

  8. git遇到的问题-- Another git process seems to be running in this repository

    执行git add .时,报错 fatal: Unable to create '/Users/lily/ForWork/forReBaomai/bm-fe/.git/index.lock': Fil ...

  9. Cookie的使用(14)

    一:cookie的简要介绍: (1)什么是cookie a.cookie是一种客户端的状态管理技术b.当浏览器向服务器发送请求的时候,服务器会将少量的数据以set-cookie消息头的方式发送给浏览器 ...

  10. 多个子域名前端网站调用同一个webAPI时session混用问题

    session机制: 当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id,如果已包含一个sess ...