Asp.Net Core軽快学-http Clientの進化とピット回避
7508 ワード
前言
Asp.Net Core 1.0時代、設計上の問題でHttpClientは開発者に限りない悩みをもたらし、Asp.Net Core開発チームの話では、HttpClientは多くの開発者に正しく使用されていないことに気づきました.おかげでNet Coreの絶え間ないバージョンが急速にアップグレードされた.ソリューションも浮上しており、本稿では、開発においてHttpClientを適切に使用してネットワークリクエストを行うために、各ビジネスシーンからHttpClientの様々な使用方法を分析することを試みた.
1.0時代の出来事
1.1 1.0時代にLinuxに配備されたAsp.Net Coreアプリケーションプロセスでは、新しいHttpClientオブジェクトを作成するたびに1つのソケットリソースが消費されるため、HttpClientのインスタンスを作成し続けた後に発生する異常が発生します.オブジェクトの使用が完了すると、HttpClientを手動で解放してもソケットリソースを解放できない可能性が高いためです.
1.2次のコードを考えると、遠い昔、次のコードは「ソケットリソースの枯渇」の異常をもたらす.
public HttpClient CreateHttpClient()
{
return new HttpClient();
}
//
public async Task GetData()
{
using (var client = new HttpClient())
{
var data = await client.GetAsync("https://www.cnblogs.com");
}
return null;
}
1.3次に、静的オブジェクトによるネットワークリクエストの使用方法を提案する
private static HttpClient httpClient = null;
public HttpClient CreateHttpClient()
{
if (httpClient == null)
httpClient = new HttpClient();
return httpClient;
}
1.4静的オブジェクトを使用することで、「ソケットリソースの消費」という例外を回避できますが、ホストDNSが更新されると、別の例外が受信される可能性があるという致命的な問題があります.
An error occurred while sending the request. Couldn't resolve host name An error occurred while sending the request. Couldn't resolve host name
1.5この異常指示はホスト名を解析できないが、実際には静的HttpClientオブジェクトがホストDNSの更新に伴って更新されないため、この場合、通常、サービスを再起動する必要がある.
2.HttpClientを正しく使う
2.1時間が来ました.Net Coreの2.2時代(実は2.1でいい)、StartupのようなHttpClientを依存注入で使用することを公式にお勧めします.csのConfigureServicesメソッドに次のコードを追加
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient();
}
2.2その後、コントローラに構築方法でHttpClientオブジェクトを注入して使用する
public class ValuesController : ControllerBase
{
private HttpClient httpClient;
public ValuesController(HttpClient httpClient)
{
this.httpClient = httpClient;
}
...
}
2.3新バージョンのAsp.Net Core、Asp.Net Core開発チームがHttpClientFactoryを導入
public HttpClient CreateHttpClient()
{
return HttpClientFactory.Create();
}
2.4 HttpClientFactoryの主な仕事はHttpClientオブジェクトを作成することであるが、作成過程において、HttpClientオブジェクトごとに個別のクリーンアップハンドルを作成することによって、HttpClientを追跡し、管理し、オブジェクトの使用が完了した後にネットワーク要求のリソース、すなわちソケットをタイムリーに解放できることを確保する.具体的なHttpClientFactoryの内部原理は李志章-DotNetCoreが深く理解した三HttpClientFactory類を参考することができる.
2.5さらに重要なことは、HttpClientFactoryの内部に接続ハンドルプールが管理されていることである.高同時性が到来すると、HttpClientFactory内のハンドルプール内の使用は完了するが、解放されていないハンドルは再使用される.HttpClientFactoryを使用するが、Create()は毎回新しいHttpClientオブジェクトを返しますが、その背後にある管理ハンドルは多重化できます.つまり、「ソケット多重化」であり、DNSが同期更新できないという問題はありません.
2.6 HttpClientFactoryを使用してHttpClientオブジェクトを作成する理由がわかりました
3.タイプ化されたHttpClientクライアントの使用
3.1従来のアプリケーションとマイクロサービスのアプリケーションシーンでは、タイプ化されたクライアントを使用することができます.タイプ化されたクライアントという言葉がよく理解されていない場合は、ビジネスごとにHttpClientクライアントを単独で使用することができます.例えば、天気予報を取得したり、次のコードを考えたりすることができます.
public class WeatherService
{
private HttpClient httpClient;
public WeatherService(HttpClient httpClient)
{
this.httpClient = httpClient;
this.httpClient.BaseAddress = new Uri("http://www.weather.com.cn");
this.httpClient.Timeout = TimeSpan.FromSeconds(30);
}
public async Task GetData()
{
var data = await this.httpClient.GetAsync("/data/sk/101010100.html");
var result = await data.Content.ReadAsStringAsync();
return result;
}
}
3.2コントローラでWeatherServiceをよりよく使用するには、WeatherServiceをサービスに注入する必要があります.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient();
}
// ,
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private WeatherService weatherService;
public ValuesController(WeatherService weatherService)
{
this.weatherService = weatherService;
}
[HttpGet]
public async Task Get()
{
string result = string.Empty;
try
{
result = await weatherService.GetData();
}
catch { }
return new JsonResult(new { result });
}
}
3.3プログラムを実行すると、北京市の天気が得られます.
{
result: "{"weatherinfo":{"city":" ","cityid":"101010100","temp":"27.9","WD":" ","WS":" 3 ","SD":"28%","AP":"1002hPa","njd":" ","WSE":"<3","time":"17:55","sm":"2.1","isRadar":"1","Radar":"JC_RADAR_AZ9010_JB"}}"
}
3.4マイクロサービスにおいて、この方法はよく見られ、非常に有用である.タイプ化されたクライアントを使用することによって、構築方法にHttpClientを注入するほか、WeatherServiceに必要なものを注入することができる.さらに重要なのは、ビジネスアプリケーションの拡張戦略、管理の便利さである.
3.5 WeatherServiceタイプ化クライアントでは、毎回新しいHttpClientオブジェクトが作成されますが、内部のハンドルと他のHttpClientは同じハンドルプールを共有しているので、心配する必要はありません.
4.HttpClientへのポリシーの適用
4.1次の戦略コンポーネントは、業界で有名なPolly(ポーリー)、GitHubアドレスです.https://github.com/App-vNext/Polly
4.2再試行ポリシーを使用し、PollyのWikiサンプルコードを参照すると、非常に簡単に使用できます.
まずNuGetからパケットPolly Pollyを参照する必要がある.Extensions.Http
4.3 Startup.cs ConfigureServicesメソッドへのポリシーの適用
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient()
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
.AddPolicyHandler(policy =>
{
return HttpPolicyExtensions.HandleTransientHttpError()
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(2),
(exception, timeSpan, retryCount, context) =>
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(" :{0} | {1} ", timeSpan, retryCount);
Console.ForegroundColor = ConsoleColor.Gray;
});
});
}
4.4以上のコードは、要求に誤りが発生した場合、3回再試行し、毎回2秒ごとに、高い同時要求に対して、再試行要求間隔はランダム値を使用することを推奨する
締めくくり
サンプルコードのダウンロード
https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.HttpClientDemo