日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HttpClientFactory 使用说明 及 对 HttpClient 的回顾和对比

發布時間:2023/12/4 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HttpClientFactory 使用说明 及 对 HttpClient 的回顾和对比 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 C# 中,平時我們在使用 HttpClient 的時候,會將 HttpClient 包裹在 using 內部進行聲明和初始化,如:

using(var httpClient = new HttpClient()){ }

至于為什么?無外乎是:項目代碼中就是這樣寫的,依葫蘆畫瓢/別人就是這樣用的/在微軟官方的 ASP.NET 教程中也是這么干的。

說的技術范點:當你使用繼承了 IDisposable 接口的對象時,建議在 using 代碼塊中聲明和初始化,當 using 代碼段執行完成后,會自動釋放該對象而不需要手動進行顯示 Dispose 操作。

但這里,HttpClient 這個對象有點特殊,雖然繼承了 IDisposable 接口,但它是可以被共享的(或者說可以被復用),且線程安全。從項目經驗來看,倒是建議在整個應用的生命周期內,復用 HttpClient 實例,而不是每次 RPC 請求的時候就實例化一個。(之前在優化公司一個 web 項目的時候,也曾經因為 HttpClient 載過一次坑,后面我會進行簡述。)

我們先來用個簡單的例子做下測試,看為什么不要每次 RPC 請求都實例化一個 HttpClient:

public class Program { static void Main(string[] args) { HttpAsync(); Console.WriteLine("Hello World!"); Console.Read(); } public static async void HttpAsync() { for (int i = 0; i < 10; i++) { using (var client = new HttpClient()) { var result = await client.GetAsync("http://www.baidu.com"); Console.WriteLine($"{i}:{result.StatusCode}"); } } } }

運行項目輸出結果后,通過 netstate 查看下 TCP 連接情況:

  • 雖然項目已經運行結束,但是連接依然存在,狀態為 "TIME_WAIT"(繼續等待看是否還有延遲的包會傳輸過來。)。

    默認在 windows 下,TIME_WAIT 狀態將會使系統將會保持該連接 240s。

  • 這里也就引出了我上面說的載過的一次坑:在高并發的情況下,連接來不及釋放,socket 被耗盡,耗盡之后就會出現喜聞樂見的一個錯誤:

Unable to connect to the remote serverSystem.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.

說白話:就是會出現“各種套接字問題”。(碼WCF的童鞋可能更加記憶尤新,問題追根溯源都是換湯不換藥。)

熊廠里面能夠搜索出來的解決方法,基本都是“減少超時時間”,但人為減少了超時時間會出現各種莫名其妙的錯誤。且無法避免服務器遲早崩潰的問題。

可以通過注冊表進行修改默認值:[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay])

那么如何處理這個問題?答案已經在上面說了,“復用 HttpClient”即可。如:

public class Program { private static readonly HttpClient _client = new HttpClient(); static void Main(string[] args) { HttpAsync(); Console.WriteLine("Hello World!"); Console.Read(); } public static async void HttpAsync() { for (int i = 0; i < 10; i++) { var result = await _client.GetAsync("http://www.baidu.com"); Console.WriteLine($"{i}:{result.StatusCode}"); } } }

  • 可以看到,原先 10 個連接變成了 1 個連接。(請不要在意兩次示例的目標 IP 不同---SLB 導致的,都是百度的ip)

  • 另外,因為復用了 HttpClient,每次 RPC 請求的時候,實際上還節約了創建通道的時間,在性能壓測的時候也是很明顯的提升。曾經因為這一舉動,將 web 項目的 TPS 從單臺 600 瞬間提升到了 2000+,頁面請求時間也從 1-3s 減少至 100-300ms,甚是讓測試組小伙伴膜拜(當然也包括了一些業務代碼的細調。),但知道個中緣由后,一個小改動帶來的項目性能提升。。。會讓人上癮:)

  • 至于如何創建一個靜態 HttpClient 進行復用,大家可以按項目實際來,如直接創建一個“全局”靜態對象,或者通過各類 DI 框架來創建均可。

但這么調整 HttpClient 的引用后,依然存在一些問題可能會影響到你的項目(尚未影響到我:P),如:

  • 因為是復用的 HttpClient,那么一些公共的設置就沒辦法靈活的調整了,如請求頭的自定義。

  • 因為 HttpClient 請求每個 url 時,會緩存該url對應的主機 ip,從而會導致 DNS 更新失效(TTL 失效了)

那么有沒有辦法解決 HttpClient 的這些個問題?直到我遇到了 HttpClientFactory,瞬間寫代碼幸福感倍升,也感慨新時代的童鞋們真的太幸福了,老一輩踩的坑可以“完美”規避掉了。

HttpClientFactory 優勢:

HttpClientFactory 是 ASP.NET CORE 2.1 中新增加的功能。

  • “完美”解決了我多年來遇到的這些坑,可以更加專注于業務代碼。

  • HttpClientFacotry 很高效,可以最大程度上節省系統 socket。(“JUST USE IT AND FXXK SHUT UP”:P)

  • Factory,顧名思義 HttpClientFactory 就是 HttpClient 的工廠,內部已經幫我們處理好了對 HttpClient 的管理,不需要我們人工進行對象釋放,同時,支持自定義請求頭,支持 DNS 更新等等等。

    從微軟源碼分析,HttpClient 繼承自 HttpMessageInvoker,而 HttpMessageInvoker 實質就是HttpClientHandler。

    HttpClientFactory 創建的 HttpClient,也即是 HttpClientHandler,只是這些個 HttpClient 被放到了“池子”中,工廠每次在 create 的時候會自動判斷是新建還是復用。(默認生命周期為 2min)

    還理解不了的話,可以參考 Task 和 Thread 的關系,以前碰到 HttpClient 這個問題的時候,就一直在想微軟什么時候官方出一個 HttpClient 的 Factory,雖然時隔了這么多年直到 .NET CORE 2.1 才出,但也很是興奮。

借助 ASP.NET CORE MVC,可以很方便的進行 HttpClient 的使用

實戰用法1:常規用法

統一在發布項目中聲明和配置。

1. 在 Startup.cs 中進行注冊

public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("client_1",config=> { config.BaseAddress= new Uri("http://client_1.com"); config.DefaultRequestHeaders.Add("header_1","header_1"); }); services.AddHttpClient("client_2",config=> { config.BaseAddress= new Uri("http://client_2.com"); config.DefaultRequestHeaders.Add("header_2","header_2"); }); services.AddHttpClient(); services.AddMvc().AddFluentValidation(); } }

2. 使用,這里直接以 controller 為例,其他地方自行 DI

public class TestController : ControllerBase { private readonly IHttpClientFactory _httpClient; public TestController(IHttpClientFactory httpClient) { _httpClient = httpClient; } public async Task<ActionResult> Test() { var client = _httpClient.CreateClient("client_1"); var result = await client.GetStringAsync("/page1.html"); var client2 = _httpClient.CreateClient(); var result2 = await client.GetStringAsync("http://www.site.com/XXX.html"); return null; } }

實戰用法2:使用自定義類執行 HttpClientFactory 請求

1. 自定義 HttpClientFactory 請求類

public class SampleClient{ public HttpClient Client { get; private set; } public SampleClient(HttpClient httpClient) { httpClient.BaseAddress = new Uri("https://api.SampleClient.com/"); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); Client = httpClient; }}

2. 在 Startup.cs 中 ConfigureService 方法中注冊 SampleClient,代碼如下,

services.AddHttpClient<SampleClient>();

3. 調用:

public class ValuesController : Controller{ private readonly SampleClient _sampleClient;; public ValuesController(SampleClient sampleClient) { _sampleClient = sampleClient; } [HttpGet] public async Task<ActionResult> Get() { string result = await _sampleClient.client.GetStringAsync("/"); return Ok(result); }}

實戰用法3:完全封裝 HttpClient 可以使用下面方法

1. 自定義 HttpClientFactory 請求類

public interface ISampleClient{ Task<string> GetData();} public class SampleClient : ISampleClient{ private readonly HttpClient _client; public SampleClient(HttpClient httpClient) { httpClient.BaseAddress = new Uri("https://api.SampleClient.com/"); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); _client = httpClient; } public async Task<string> GetData() { return await _client.GetStringAsync("/"); }}

2. 在 Startup.cs 中 ConfigureService 方法中注冊 SampleClient,代碼如下,

services.AddHttpClient<ISampleClient, SampleClient>();

3. 調用:

public class ValuesController : Controller{ private readonly ISampleClient _sampleClient;; public ValuesController(ISampleClient sampleClient) { _sampleClient = sampleClient; } [HttpGet] public async Task<ActionResult> Get() { string result = await _sampleClient.GetData(); return Ok(result); }}

總結

以上是生活随笔為你收集整理的HttpClientFactory 使用说明 及 对 HttpClient 的回顾和对比的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。