|
|
@ -28,6 +28,12 @@
|
|
|
|
"cell_type": "code",
|
|
|
|
"cell_type": "code",
|
|
|
|
"execution_count": null,
|
|
|
|
"execution_count": null,
|
|
|
|
"metadata": {
|
|
|
|
"metadata": {
|
|
|
|
|
|
|
|
"dotnet_interactive": {
|
|
|
|
|
|
|
|
"language": "csharp"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"polyglot_notebook": {
|
|
|
|
|
|
|
|
"kernelName": "csharp"
|
|
|
|
|
|
|
|
},
|
|
|
|
"vscode": {
|
|
|
|
"vscode": {
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -43,16 +49,18 @@
|
|
|
|
"global using System.Text;\n",
|
|
|
|
"global using System.Text;\n",
|
|
|
|
"global using System.Linq;\n",
|
|
|
|
"global using System.Linq;\n",
|
|
|
|
"global using System.IO;\n",
|
|
|
|
"global using System.IO;\n",
|
|
|
|
|
|
|
|
"global using System.Threading;\n",
|
|
|
|
|
|
|
|
"global using System.Threading.Tasks;\n",
|
|
|
|
"\n",
|
|
|
|
"\n",
|
|
|
|
"global using HttpClientStudy.Core;\n",
|
|
|
|
"global using HttpClientStudy.Core;\n",
|
|
|
|
"\n",
|
|
|
|
"\n",
|
|
|
|
"var global_queryDomain=\"soft.pwidc.cn\";\n",
|
|
|
|
"var global_queryDomain = \"soft.pwidc.cn\";\n",
|
|
|
|
"var global_queryPort=80;\n",
|
|
|
|
"var global_queryPort = 80;\n",
|
|
|
|
"var global_queryBaseUrl = $\"http://{global_queryDomain}:{global_queryPort}\";\n",
|
|
|
|
"var global_queryBaseUrl = $\"http://{global_queryDomain}:{global_queryPort}\";\n",
|
|
|
|
"\n",
|
|
|
|
"\n",
|
|
|
|
"var global_ips =Dns.GetHostAddresses(global_queryDomani);\n",
|
|
|
|
"var global_ips = Dns.GetHostAddresses(global_queryDomain);\n",
|
|
|
|
"var global_queryIp = ips.First().ToString();\n",
|
|
|
|
"var global_queryIp = global_ips.First().ToString();\n",
|
|
|
|
"var global_netstat_filter = $\"{queryIp}:{global_queryPort}\";"
|
|
|
|
"var global_netstat_filter = $\"{global_queryIp}:{global_queryPort}\";"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -66,6 +74,12 @@
|
|
|
|
"cell_type": "code",
|
|
|
|
"cell_type": "code",
|
|
|
|
"execution_count": null,
|
|
|
|
"execution_count": null,
|
|
|
|
"metadata": {
|
|
|
|
"metadata": {
|
|
|
|
|
|
|
|
"dotnet_interactive": {
|
|
|
|
|
|
|
|
"language": "csharp"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"polyglot_notebook": {
|
|
|
|
|
|
|
|
"kernelName": "csharp"
|
|
|
|
|
|
|
|
},
|
|
|
|
"vscode": {
|
|
|
|
"vscode": {
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -557,6 +571,12 @@
|
|
|
|
"cell_type": "code",
|
|
|
|
"cell_type": "code",
|
|
|
|
"execution_count": null,
|
|
|
|
"execution_count": null,
|
|
|
|
"metadata": {
|
|
|
|
"metadata": {
|
|
|
|
|
|
|
|
"dotnet_interactive": {
|
|
|
|
|
|
|
|
"language": "csharp"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"polyglot_notebook": {
|
|
|
|
|
|
|
|
"kernelName": "csharp"
|
|
|
|
|
|
|
|
},
|
|
|
|
"vscode": {
|
|
|
|
"vscode": {
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -597,6 +617,141 @@
|
|
|
|
"source": [
|
|
|
|
"source": [
|
|
|
|
"## 3、推荐使用方式"
|
|
|
|
"## 3、推荐使用方式"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
|
|
|
"metadata": {},
|
|
|
|
|
|
|
|
"source": [
|
|
|
|
|
|
|
|
"总则:\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
" 一、 应使用长期客户端(静态对象、单例等),并设置 PooledConnectionLifetime。这能解决DNS问题和套接字耗尽问题。\n",
|
|
|
|
|
|
|
|
" \n",
|
|
|
|
|
|
|
|
" 二、 使用 IHttpClientFactory 创建的短期客户端:"
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
|
|
|
"metadata": {},
|
|
|
|
|
|
|
|
"source": [
|
|
|
|
|
|
|
|
"+ 在 .NET Core 和 .NET 5+ 中:\n",
|
|
|
|
|
|
|
|
" \n",
|
|
|
|
|
|
|
|
" + 根据预期的 DNS 更改,使用 static 或 singletonHttpClient 实例,并将 PooledConnectionLifetime 设置为所需间隔(例如 2 分钟)。 这可以解决端口耗尽和 DNS 更改两个问题,而且不会增加 IHttpClientFactory 的开销。 如果需要模拟处理程序,可以单独注册它。\n",
|
|
|
|
|
|
|
|
" \n",
|
|
|
|
|
|
|
|
" + 使用 IHttpClientFactory,可以针对不同的用例使用多个以不同方式配置的客户端。 但请注意,工厂创建的客户端生存期较短,一旦创建客户端,工厂就不再可以控制它。\n",
|
|
|
|
|
|
|
|
" 工厂合并 HttpMessageHandler 实例,如果其生存期尚未过期,则当工厂创建新的 HttpClient 实例时,可以从池中重用处理程序。 这种重用避免了任何套接字耗尽问题。\n",
|
|
|
|
|
|
|
|
" 如果需要 IHttpClientFactory 提供的可配置性,我们建议使用类型化客户端方法。\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
"+ 在 .NET Framework 中,使用 IHttpClientFactory 管理 HttpClient 实例。 如果不使用工厂,而是改为自行为每个请求创建新的客户端实例,则可能耗尽可用的端口。 \n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
"`提示`: 如果应用需要 Cookie,请考虑禁用自动 Cookie 处理或避免使用 IHttpClientFactory。 共用 HttpMessageHandler 实例会导致共享 CookieContainer 对象。 意外的 CookieContainer 对象共享通常会导致错误的代码。"
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
|
|
|
"execution_count": null,
|
|
|
|
|
|
|
|
"metadata": {
|
|
|
|
|
|
|
|
"dotnet_interactive": {
|
|
|
|
|
|
|
|
"language": "csharp"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"polyglot_notebook": {
|
|
|
|
|
|
|
|
"kernelName": "csharp"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"vscode": {
|
|
|
|
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"outputs": [],
|
|
|
|
|
|
|
|
"source": [
|
|
|
|
|
|
|
|
"{ //不推荐的示例\n",
|
|
|
|
|
|
|
|
" int requestCount =0;\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
" //这会建立10个 HttpClient \n",
|
|
|
|
|
|
|
|
" //尽管使用了Using,不过Using只保证应用进程释放实例;但是http请求是跨操作系统、跨网络的操作,调用Using的进程管不了操作系统,更管不了网络。\n",
|
|
|
|
|
|
|
|
" //如果把循环次数加大到 65535 就会一定导致夏套接字耗尽(2000以很可能就会出现)。\n",
|
|
|
|
|
|
|
|
" Parallel.For(0,10,async (a,b)=>\n",
|
|
|
|
|
|
|
|
" {\n",
|
|
|
|
|
|
|
|
" using (var client = new HttpClient())\n",
|
|
|
|
|
|
|
|
" {\n",
|
|
|
|
|
|
|
|
" _ = await client.GetAsync(global_queryBaseUrl);\n",
|
|
|
|
|
|
|
|
" } \n",
|
|
|
|
|
|
|
|
" Interlocked.Add(ref requestCount, 1);\n",
|
|
|
|
|
|
|
|
" });\n",
|
|
|
|
|
|
|
|
"}\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
"{ //使用长期客户端\n",
|
|
|
|
|
|
|
|
" using (var client = new HttpClient())\n",
|
|
|
|
|
|
|
|
" {\n",
|
|
|
|
|
|
|
|
" for(int i=0; i<10; i++)\n",
|
|
|
|
|
|
|
|
" {\n",
|
|
|
|
|
|
|
|
" //n次调用,均使用同一个 HttpClient 实例\n",
|
|
|
|
|
|
|
|
" _ = await client.GetAsync(global_queryBaseUrl);\n",
|
|
|
|
|
|
|
|
" }\n",
|
|
|
|
|
|
|
|
" }// 所有调用完成,才释放 HttpClient 实例\n",
|
|
|
|
|
|
|
|
"}"
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
|
|
|
"metadata": {},
|
|
|
|
|
|
|
|
"source": [
|
|
|
|
|
|
|
|
"## 4、静态客户端的复原能力"
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
|
|
|
"execution_count": 62,
|
|
|
|
|
|
|
|
"metadata": {
|
|
|
|
|
|
|
|
"vscode": {
|
|
|
|
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"outputs": [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"ename": "Error",
|
|
|
|
|
|
|
|
"evalue": "Command cancelled.",
|
|
|
|
|
|
|
|
"output_type": "error",
|
|
|
|
|
|
|
|
"traceback": [
|
|
|
|
|
|
|
|
"Command cancelled."
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
"source": [
|
|
|
|
|
|
|
|
"#r \"nuget:Polly\"\n",
|
|
|
|
|
|
|
|
"#r \"nuget:Microsoft.Extensions.Http.Resilience\"\n",
|
|
|
|
|
|
|
|
"using System;\n",
|
|
|
|
|
|
|
|
"using System.Net.Http;\n",
|
|
|
|
|
|
|
|
"using Microsoft.Extensions.Http;\n",
|
|
|
|
|
|
|
|
"using Microsoft.Extensions.Http.Resilience;\n",
|
|
|
|
|
|
|
|
"using Polly;\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
"{\n",
|
|
|
|
|
|
|
|
" var retryPipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()\n",
|
|
|
|
|
|
|
|
" .AddRetry(new HttpRetryStrategyOptions\n",
|
|
|
|
|
|
|
|
" {\n",
|
|
|
|
|
|
|
|
" BackoffType = DelayBackoffType.Exponential,\n",
|
|
|
|
|
|
|
|
" MaxRetryAttempts = 3\n",
|
|
|
|
|
|
|
|
" })\n",
|
|
|
|
|
|
|
|
" .Build();\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
" var socketHandler = new SocketsHttpHandler\n",
|
|
|
|
|
|
|
|
" {\n",
|
|
|
|
|
|
|
|
" PooledConnectionLifetime = TimeSpan.FromMinutes(15)\n",
|
|
|
|
|
|
|
|
" };\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
" #pragma warning disable EXTEXP0001\n",
|
|
|
|
|
|
|
|
" var resilienceHandler = new ResilienceHandler(retryPipeline)\n",
|
|
|
|
|
|
|
|
" {\n",
|
|
|
|
|
|
|
|
" InnerHandler = socketHandler,\n",
|
|
|
|
|
|
|
|
" };\n",
|
|
|
|
|
|
|
|
" #pragma warning restore EXTEXP0001\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
" var httpClient = new HttpClient(resilienceHandler);\n",
|
|
|
|
|
|
|
|
"\n",
|
|
|
|
|
|
|
|
" var response = await httpClient.GetAsync(\"https://www.baidu.com\");\n",
|
|
|
|
|
|
|
|
" var htmlText = await response.Content.ReadAsStringAsync();\n",
|
|
|
|
|
|
|
|
" Console.WriteLine($\"共有{htmlText.Length}个字符\");\n",
|
|
|
|
|
|
|
|
"}\n"
|
|
|
|
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
],
|
|
|
|
"metadata": {
|
|
|
|
"metadata": {
|
|
|
|