使用YARP的IHttpForwarder转发请求速度过慢的优化求助
从你的遥测日志和代码来看,耗时主要集中在两个阶段:YARP向目标服务器发送请求并接收响应头(约224ms),以及读取目标服务器响应内容(约204ms),结合Postman的基准耗时,给出以下具体优化方案:
1. 复用HttpMessageInvoker与SocketsHttpHandler
你当前在Controller构造函数中每次创建新的HttpMessageInvoker和SocketsHttpHandler,这会导致每次请求都重新建立TCP连接,完全浪费了连接池的优势。正确的做法是将HttpMessageInvoker注册为单例,让所有请求复用同一个连接池:
// 在Program.cs中注册 builder.Services.AddSingleton<HttpMessageInvoker>(sp => { return new HttpMessageInvoker(new SocketsHttpHandler() { UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, UseCookies = false, ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current), // 连接池优化配置 MaxConnectionsPerServer = 100, PooledConnectionLifetime = TimeSpan.FromMinutes(5), PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), EnableHttp2 = true }); }); // 在Controller中注入 public TestController(IHttpForwarder forwarder, HttpMessageInvoker httpClient) { _forwarder = forwarder; _httpClient = httpClient; _transformer = new CustomTransformer(); _requestOptions = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(100) }; }
2. 开启自动解压减少响应体积
你的SocketsHttpHandler设置了AutomaticDecompression = DecompressionMethods.None,如果目标服务器返回的是压缩内容(如Gzip),YARP会读取原始的大体积内容,增加读取耗时。Postman默认会自动处理解压,所以这部分差异会直接体现在耗时上。建议开启自动解压:
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
同时确保请求头中包含Accept-Encoding: gzip, deflate,可以在CustomTransformer中添加:
public override ValueTask TransformRequestAsync(HttpContext context, HttpRequestMessage proxyRequest, string destinationPrefix) { proxyRequest.Headers.AcceptEncoding.ParseAdd("gzip, deflate"); return base.TransformRequestAsync(context, proxyRequest, destinationPrefix); }
3. 优化响应内容传输逻辑
从日志看,响应内容有510KB,Read time: 0.204秒是YARP从目标服务器读取内容的时间。可以调整YARP的响应缓冲区大小,减少IO操作次数:
在注册YARP时配置HttpForwarderOptions:
builder.Services.AddHttpForwarder(options => { options.ResponseBodyBufferSize = 65536; // 64KB缓冲区,默认可能更小 });
另外,如果你的CustomTransformer没有修改响应内容的需求,可以直接跳过额外的内容处理,让YARP直接转发响应流,避免内存拷贝开销。
4. 排查CustomTransformer的额外开销
如果你的CustomTransformer中实现了复杂的请求/响应转换逻辑(比如修改Body、遍历并修改大量Headers),会增加额外的耗时。可以暂时替换为默认的Transformer测试:
// 替换为默认Transformer _transformer = new Transformer();
如果耗时明显下降,再逐一排查Transformer中的代码是否有可以优化的地方。
5. 强制使用HTTP/2
Postman通常默认支持HTTP/2,而SocketsHttpHandler默认可能会协商使用HTTP/1.1。强制开启HTTP/2可以利用多路复用特性,减少连接建立的开销:
new SocketsHttpHandler() { // 其他配置 EnableHttp2 = true }
内容的提问来源于stack exchange,提问作者HC1122

