From c4cd9f08d6f01ce10180edb569af51f456886722 Mon Sep 17 00:00:00 2001 From: wanggaofeng <15601716045@163.com> Date: Fri, 12 Jan 2024 14:26:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HttpClientStudy.Core/PipelineClient.cs | 106 ++++++++++++++++++ .../DelegatingHandlerTest.cs | 68 +++++++++++ .../HttpClientQuestTest.cs | 77 +++++++++++++ .../PipelineClientTest.cs | 24 ++++ 4 files changed, 275 insertions(+) create mode 100644 HttpClientStudy.Core/PipelineClient.cs create mode 100644 HttpClientStudy.UnitTest/DelegatingHandlerTest.cs create mode 100644 HttpClientStudy.UnitTest/HttpClientQuestTest.cs create mode 100644 HttpClientStudy.UnitTest/PipelineClientTest.cs diff --git a/HttpClientStudy.Core/PipelineClient.cs b/HttpClientStudy.Core/PipelineClient.cs new file mode 100644 index 0000000..007ff05 --- /dev/null +++ b/HttpClientStudy.Core/PipelineClient.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace HttpClientStudy.Core +{ + /// + /// 管道方式 HttpClient客户端 + /// + public class PipelineClient + { + public List HttpMessageHandlers { get; set; } + + public PipelineClient() + { + HttpMessageHandlers = new List() + { + new CustomHeadersHandler(), + new LoggingHandler(), + }; + + } + + public PipelineClient(List httpMessageHandlers) + { + HttpMessageHandlers = httpMessageHandlers; + if (httpMessageHandlers == null || httpMessageHandlers?.Count == 0) + { + HttpMessageHandlers = new List(); + } + } + + public void AddDelegatingHandler(DelegatingHandler handler) + { + this.HttpMessageHandlers.Add(handler); + } + + public HttpClient CreateHttpClient() + { + //创建处理器链 + + //最终处理器:最后一个请求处理程序,默认是SocketsHttpHandler. + HttpMessageHandler currentPipeLine = new SocketsHttpHandler() + { + //自定义配置 + PooledConnectionLifetime = TimeSpan.FromSeconds(120), + }; + //pipeline = new CustomHeadersHandler() { InnerHandler = pipeline}; + //pipeline = new LoggingHandler () { InnerHandler = pipeline}; + + //倒序组装:完成后执行顺序与注册顺序一致 + HttpMessageHandlers.Reverse(); + for (int i = 0; i < HttpMessageHandlers.Count; i++) + { + HttpMessageHandlers[i].InnerHandler = currentPipeLine; + currentPipeLine = HttpMessageHandlers[i]; + } + + // 利用管道创建HttpClient + var httpClient = new HttpClient(currentPipeLine); + + return httpClient; + } + } + + /// + /// 默认处理器1 - 添加自定义请求头 + /// + public class CustomHeadersHandler : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + // 在请求中添加自定义头部 + request.Headers.Add("X-Custom-Header", "CustomValue"); + + // 调用管道中的下一个处理器 + return await base.SendAsync(request, cancellationToken); + + //响应信息(可不处理) + } + } + + /// + /// 默认处理器1 - 简单的日志记录 + /// + public class LoggingHandler : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + // 记录请求信息 + Console.WriteLine($"Request: {request.Method} {request.RequestUri}"); + + // 调用管道中的下一个处理器,并获取响应 + var response = await base.SendAsync(request, cancellationToken); + + // 记录响应信息 + Console.WriteLine($"Response: {response.StatusCode}"); + + return response; + } + } + +} diff --git a/HttpClientStudy.UnitTest/DelegatingHandlerTest.cs b/HttpClientStudy.UnitTest/DelegatingHandlerTest.cs new file mode 100644 index 0000000..3d1a769 --- /dev/null +++ b/HttpClientStudy.UnitTest/DelegatingHandlerTest.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +using Microsoft.Extensions.Logging; + +namespace HttpClientStudy.UnitTest +{ + public class DelegatingHandlerTest + { + private readonly ITestOutputHelper _logger; + public DelegatingHandlerTest(ITestOutputHelper outputHelper) + { + _logger = outputHelper; + } + + [Fact] + public async Task Test() + { + //构建管道 + var handler = new HandlerA() + { + //相当于下一个中间件(管道) + InnerHandler = new HandlerB() + }; + + HttpClient httpClient = new HttpClient(handler); + + var sd = await httpClient.GetAsync("http://127.0.0.1"); + var contentText = await sd.Content.ReadAsStringAsync(); + + _logger.WriteLine(contentText); + } + } + + public class HandlerB : DelegatingHandler + { + protected override async Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) + { + InnerHandler = new SocketsHttpHandler() { }; + + //请求前业务 + + var response = await base.SendAsync(request, cancellationToken); + + //响应后业务 + + return response; + } + } + + public class HandlerA : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + //请求前业务 + + var response = await base.SendAsync(request, cancellationToken); + + //响应后业务 + + return response; + } + } +} diff --git a/HttpClientStudy.UnitTest/HttpClientQuestTest.cs b/HttpClientStudy.UnitTest/HttpClientQuestTest.cs new file mode 100644 index 0000000..d4725d2 --- /dev/null +++ b/HttpClientStudy.UnitTest/HttpClientQuestTest.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using System.Net.Http.Json; +using System.Net; +using Newtonsoft.Json; +using Microsoft.AspNetCore.Mvc; +using System.Text.Json; +using System.Security.Authentication.ExtendedProtection; +using Newtonsoft.Json.Linq; +using System.Text.Json.Serialization.Metadata; + +namespace HttpClientStudy.UnitTest +{ + /// + /// HttpClient 常见问题测试 + /// + public class HttpClientQuestTest + { + private readonly ITestOutputHelper _logger; + + public HttpClientQuestTest(ITestOutputHelper outputHelper) + { + _logger = outputHelper; + } + + /// + /// 多HttpClient实例问题 + /// 1、HttpClient虽然继承了 IDisposable 接口,但它是可以被共享的,且线程安全。Using使用并不能立即释放 + /// 2、网络特性:虽然HttpClient客户端断开,但Web服务并不知道,连接依然存在,状态为 “ TIME_WAIT”(继续等待看是否还有延迟的包会传输过来。) + /// Windows默认为 240s + /// 引发常见问题: + /// 1、HttpClient 多实例并存,有很大资源消耗; + /// 2、在高并发的情况下,连接来不及释放, socket 被耗尽: 程序异常 + /// 3、连带着会出现“各种套接字问题” + /// + /// 查看问题: + /// 程序并发或多次请求后,通过 netstate 查看下 TCP 连接情况:有很多 TIME_WAIT 状态连接;很可能程序异常; + /// + /// 解决: + /// 1、复用HttpClient + /// 2、HttpClientFactory + /// 3、解决DNS问题可以给HttpClient传参:SocketsHttpHandler + /// + /// + [Fact] + public async Task Multi_ClientObject_Test() + { + for (int i = 0; i < 1000; i++) + { + HttpClient httpClient = new HttpClient(); + + var responseMessage = await httpClient.GetAsync("https://www.baidu.com"); + + responseMessage.EnsureSuccessStatusCode(); + } + } + + [Fact] + public async Task Multi_ClientObject2_Test() + { + HttpClient httpClient = new HttpClient() + { + BaseAddress = new Uri("https://www.baidu.com") + }; + for (int i = 0; i < 10000; i++) + { + var responseMessage = await httpClient.GetAsync(""); + + responseMessage.EnsureSuccessStatusCode(); + } + } + } +} diff --git a/HttpClientStudy.UnitTest/PipelineClientTest.cs b/HttpClientStudy.UnitTest/PipelineClientTest.cs new file mode 100644 index 0000000..8564224 --- /dev/null +++ b/HttpClientStudy.UnitTest/PipelineClientTest.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HttpClientStudy.UnitTest +{ + public class PipelineClientTest + { + public PipelineClientTest() { } + + [Fact] + public async Task Test() + { + HttpClient client = new PipelineClient().CreateHttpClient(); + + var r = await client.GetAsync("https://www.baidu.com"); + r.EnsureSuccessStatusCode(); + + + } + } +}