網(wǎng)站首頁 編程語言 正文
ASP.NET?Core使用IHttpClientFactory發(fā)出HTTP請(qǐng)求_基礎(chǔ)應(yīng)用
作者:暗斷腸 ? 更新時(shí)間: 2022-06-08 編程語言1.HttpClient類使用存在的問題
HttpClient類的使用所存在的問題,百度搜索的文章一大堆,好多都是單純文字描述,讓人感覺不太好理解,為了更好理解HttpClient使用存在的問題,下面讓我們通過代碼跟示例來描述。
using(var client = new HttpClient())
傳統(tǒng)關(guān)閉連接方法如上述代碼所示,但當(dāng)使用using語句釋放HttpClient對(duì)象的時(shí)候,套接字(socket)也不會(huì)立即釋放,下面我們通過請(qǐng)求aspnetmonsters站點(diǎn)的示例來驗(yàn)證下:
class Program { static void Main(string[] args) { Console.WriteLine("Starting connections"); var g = GetAsync(); g.Wait(); Console.WriteLine("Connections done"); Console.ReadKey(); } static async Task GetAsync() { for (int i = 0; i < 5; i++) { using (var client = new HttpClient()) { var result = await client.GetAsync("http://aspnetmonsters.com/"); Console.WriteLine(result.StatusCode); } } } }
輸出結(jié)果:
控制臺(tái)打印出五條請(qǐng)求站點(diǎn)返回狀態(tài)的信息,下面我們通過netstat工具打印出五個(gè)請(qǐng)求連接套接字狀態(tài):
應(yīng)用程序已經(jīng)運(yùn)行結(jié)束了(結(jié)束連接),但是打印結(jié)果顯示連接狀態(tài)仍然是TIME_WAIT,也就是說在此狀態(tài)期間仍然在觀察是否有數(shù)據(jù)包進(jìn)入連接(如果連接等待中有任何數(shù)據(jù)包仍然會(huì)通過),因?yàn)樗鼈兛赡茉谀硞€(gè)地方被網(wǎng)絡(luò)延遲。
Windows將在此狀態(tài)下保持連接240秒(由其設(shè)置[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay])。Windows可以快速打開新套接字的速度有限,因此如果您耗盡連接池,那么您可能會(huì)看到如下錯(cuò)誤:
而怎么做才可以減少套接字的浪費(fèi)呢?我們?cè)谏鲜龃a中把每次循環(huán)中創(chuàng)建的HttpClient對(duì)象拉到Main外定義為一個(gè)共享的靜態(tài)實(shí)例:
class Program { private static HttpClient client = new HttpClient(); static void Main(string[] args) { Console.WriteLine("Starting connections"); var g = GetAsync(); g.Wait(); Console.WriteLine("Connections done"); Console.ReadKey(); } static async Task GetAsync() { for (int i = 0; i < 5; i++) { var result = await client.GetAsync("http://aspnetmonsters.com/"); Console.WriteLine(result.StatusCode); } } }
應(yīng)用程序運(yùn)動(dòng)完畢之后,我們?cè)偻ㄟ^netstat工具打印出五個(gè)請(qǐng)求連接套接字狀態(tài),這時(shí)候會(huì)看到信息如下:
通過共享一個(gè)實(shí)例,減少了套接字的浪費(fèi),實(shí)際上由于套接字重用而傳輸快一點(diǎn)。
總結(jié):
- 在創(chuàng)建HttpClient實(shí)例的時(shí)候,最好是靜態(tài)(static )實(shí)例。
- 不要用using包裝HttpClient對(duì)象。
在.NET Core 2.1版本之后引入的 HttpClientFactory解決了HttpClient的所有痛點(diǎn)。有了 HttpClientFactory,我們不需要關(guān)心如何創(chuàng)建HttpClient,又如何釋放它。通過它可以創(chuàng)建具有特定業(yè)務(wù)的HttpClient,而且可以很友好的和 DI 容器結(jié)合使用,更為靈活。下面以 ASP.NET Core為例介紹HttpClientFactory的四種使用方式。
2.HttpClientFactory 的多種使用方式
可以通過多種使用方式在應(yīng)用程序中使用HttpClientFactory。
2.1使用基本用法
在Startup.ConfigureServices方法中,通過在IServiceCollection上調(diào)用AddHttpClient擴(kuò)展方法可以注冊(cè)IHttpClientFactory服務(wù)。
services.AddHttpClient();
注冊(cè)服務(wù)后,我們新建BasicUsageModel類使用IHttpClientFactory創(chuàng)建HttpClient實(shí)例:
public class BasicUsageModel { private readonly IHttpClientFactory _clientFactory; public IEnumerableBranches { get; private set; } public bool GetBranchesError { get; private set; } public BasicUsageModel(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } public async Task OnGet() { var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/aspnet/AspNetCore.Docs/branches"); request.Headers.Add("Accept", "application/vnd.github.v3+json"); request.Headers.Add("User-Agent", "HttpClientFactory-Sample"); var client = _clientFactory.CreateClient(); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { Branches = await response.Content .ReadAsAsync >(); } else { GetBranchesError = true; Branches = Array.Empty (); } } } public class GitHubBranch { public string name { get; set; } }
以這種方式直接在使用IHttpClientFactory的類中調(diào)用CreateClient方法創(chuàng)建HttpClient實(shí)例。然后在Controller中調(diào)用BasicUsageModel類:
public class HomeController : Controller { private readonly IHttpClientFactory _clientFactory; public HomeController(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } public IActionResult Index() { BasicUsageModel model = new BasicUsageModel(_clientFactory); var task = model.OnGet(); task.Wait(); Listlist = model.Branches.ToList(); return View(list); } }
2.2使用命名客戶端
如果應(yīng)用程序需要有許多不同的HttpClient用法(每種用法的服務(wù)配置都不同),可以視情況使用命名客戶端。可以在HttpClient中注冊(cè)時(shí)指定命名Startup.ConfigureServices的配置。
services.AddHttpClient("github", c => { c.BaseAddress = new Uri("https://api.github.com/"); // Github API versioning c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); // Github requires a user-agent c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); });
上面的代碼調(diào)用AddHttpClient,同時(shí)提供名稱“github”。此客戶端應(yīng)用了一些默認(rèn)配置,也就是需要基址和兩個(gè)標(biāo)頭來使用GitHub API。每次調(diào)用CreateClient時(shí),都會(huì)創(chuàng)建HttpClient 的新實(shí)例,并調(diào)用配置操作。要使用命名客戶端,可將字符串參數(shù)傳遞到CreateClient。指定要?jiǎng)?chuàng)建的客戶端的名稱:
public class NamedClientModel : PageModel { private readonly IHttpClientFactory _clientFactory; public IEnumerablePullRequests { get; private set; } public bool GetPullRequestsError { get; private set; } public bool HasPullRequests => PullRequests.Any(); public NamedClientModel(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } public async Task OnGet() { var request = new HttpRequestMessage(HttpMethod.Get, "repos/aspnet/AspNetCore.Docs/pulls"); var client = _clientFactory.CreateClient("github"); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { PullRequests = await response.Content .ReadAsAsync >(); } else { GetPullRequestsError = true; PullRequests = Array.Empty (); } } } public class GitHubPullRequest { public string url { get; set; } public int? id { get; set; } public string node_id { get; set; } }
在上述代碼中,請(qǐng)求不需要指定主機(jī)名。可以僅傳遞路徑,因?yàn)椴捎昧藶榭蛻舳伺渲玫幕贰T贑ontroller中調(diào)用方法如上個(gè)示例。
2.3使用類型化客戶端
什么是“類型化客戶端”?它只是DefaultHttpClientFactory注入時(shí)配置的HttpClient。
下圖顯示了如何將類型化客戶端與HttpClientFactory結(jié)合使用:
類型化客戶端提供與命名客戶端一樣的功能,不需要將字符串用作密鑰。它們提供單個(gè)地址來配置特定HttpClient并與其進(jìn)行交互。例如,單個(gè)類型化客戶端可能用于單個(gè)后端終結(jié)點(diǎn),并封裝此終結(jié)點(diǎn)的所有處理邏輯。另一個(gè)優(yōu)勢是它們使用 DI 且可以被注入到應(yīng)用中需要的位置。
類型化客戶端在構(gòu)造函數(shù)中接收HttpClient參數(shù):
public class GitHubService { public HttpClient Client { get; } public GitHubService(HttpClient client) { client.BaseAddress = new Uri("https://api.github.com/"); // GitHub API versioning client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); // GitHub requires a user-agent client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); Client = client; } public async Task> GetAspNetDocsIssues() { var response = await Client.GetAsync( "/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc"); response.EnsureSuccessStatusCode(); var result = await response.Content .ReadAsAsync >(); return result; } } public class GitHubIssue { public string url { get; set; } public int? id { get; set; } public string node_id { get; set; } }
在上述代碼中,配置轉(zhuǎn)移到了類型化客戶端中。HttpClient對(duì)象公開為公共屬性。可以定義公開HttpClient功能的特定于API的方法。GetAspNetDocsIssues方法從GitHub存儲(chǔ)庫封裝查詢和分析最新待解決問題所需的代碼。
要注冊(cè)類型化客戶端,可在Startup.ConfigureServices中使用通用的AddHttpClient擴(kuò)展方法,指定類型化客戶端類:
services.AddHttpClient();
使用DI將類型客戶端注冊(cè)為暫時(shí)客戶端。可以直接插入或使用類型化客戶端:
public class TypedClientModel : PageModel { private readonly GitHubService _gitHubService; public IEnumerableLatestIssues { get; private set; } public bool HasIssue => LatestIssues.Any(); public bool GetIssuesError { get; private set; } public TypedClientModel(GitHubService gitHubService) { _gitHubService = gitHubService; } public async Task OnGet() { try { LatestIssues = await _gitHubService.GetAspNetDocsIssues(); } catch (HttpRequestException) { GetIssuesError = true; LatestIssues = Array.Empty (); } } }
原文鏈接:https://www.cnblogs.com/wzk153/p/10945313.html
相關(guān)推薦
- 2022-10-14 Springboot整合Netty提供WebSocket服務(wù)
- 2022-04-05 C語言實(shí)現(xiàn)自動(dòng)售貨機(jī)_C 語言
- 2022-08-10 Pandas?sample隨機(jī)抽樣的實(shí)現(xiàn)_python
- 2022-06-07 SQL?Server內(nèi)存機(jī)制詳解_MsSql
- 2022-09-17 python?Pandas之DataFrame索引及選取數(shù)據(jù)_python
- 2022-12-21 Flutter?runApp到渲染上屏分析詳解_Android
- 2022-04-22 arm-linux使用qt開發(fā)并加入openssl
- 2023-01-08 Android?Application的使用全面解析_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支