c# YARP 反向代理的并发模型和性能调优

来源:这里教程网 时间:2026-02-21 17:37:44 作者:

YARP 的并发模型基于 ASP.NET Core 的 Kestrel + HttpClient 组合

YARP 本身不维护独立的线程池或连接池,它复用 ASP.NET Core 底层的

Kestrel
服务器处理入站请求,并通过
HttpClient
(默认是
HttpMessageInvoker
)发起出站代理请求。这意味着它的并发能力直接受限于:
Kestrel
的连接队列、
ThreadPool
的工作线程调度、以及后端
HttpClient
实例的连接复用与超时策略。

常见错误现象包括:高并发下大量

503 Service Unavailable
、后端服务日志显示连接被拒绝、
HttpClient
SocketException: Too many open files
(Linux)或
WinHttpSendRequest failed: 12029
(Windows)。

HttpClient
必须复用——每个路由不应 new 新实例;推荐使用
IHttpClientFactory
注册命名客户端
避免在
Transforms
中做同步阻塞操作(如
File.ReadAllText
),会卡住 Kestrel 线程
YARP 默认启用
ConnectionPool
,但池大小由
HttpClientHandler.MaxConnectionsPerServer
控制,默认是
int.MaxValue
(.NET 6+),实际受限于 OS 文件句柄数

关键配置项:HttpClientHandler 和 YARP Cluster 设置

性能瓶颈往往出现在出站连接控制不当。YARP 的

Cluster
配置决定了如何将请求分发到多个后端节点,而底层
HttpClientHandler
决定了单个后端能维持多少并发连接。

以下配置需在

appsettings.json
或代码中显式设置:

HttpClientHandler.MaxConnectionsPerServer
:建议设为
100~500
(视后端吞吐能力而定),过高易触发 OS 限制;过低则连接复用率下降,增加 TLS 握手开销
HttpClient.Timeout
:YARP 默认为
100s
,但应按后端 SLA 缩短(如
30s
),避免请求堆积
Cluster.LoadBalancingPolicy
:生产环境慎用
Random
,优先选
PowerOfTwoChoices
(降低尾延迟)或
LeastRequests
(需启用健康检查)
HealthCheck.Active.HealthCheckTimeout
:若开启主动健康检查,超时值必须小于
HttpClient.Timeout
,否则健康检查自身会拖慢故障发现

诊断高并发问题的三个必查点

当压测出现吞吐不升反降、延迟毛刺增多时,不要只看 YARP 日志。真正瓶颈常藏在底层资源上。

检查
dotnet-counters monitor -p <pid> --counters Microsoft.AspNetCore.Hosting,Microsoft.Net.Http.Connections</pid>
,重点关注
ActiveRequests
CurrentConnections
是否持续高位
确认后端服务是否启用了连接复用(如 Nginx 的
keepalive 32
)、TLS session resumption 是否开启(减少握手耗时)
Linux 下运行
lsof -p <pid> | wc -l</pid>
查看进程打开文件数,对比
ulimit -n
;若接近上限,需调大
MaxConnectionsPerServer
或优化连接生命周期

一个典型的高性能 YARP HttpClient 配置示例

下面是在

Program.cs
中注册带精细控制的
HttpClient
的方式,适用于多数中高流量场景:

builder.Services.AddReverseProxy()
    .ConfigureHttpClient((context, client) =>
    {
        client.Timeout = TimeSpan.FromSeconds(30);
        client.DefaultRequestHeaders.UserAgent.ParseAdd("YARP/2.0");
    })
    .AddTransforms(builder =>
    {
        builder.UseRequestTransform(async transformContext =>
        {
            // 避免在此处 await Task.Delay 或 I/O 操作
            transformContext.ProxyRequest.Headers.Host = transformContext.Destination.ParsedUri.Authority;
        });
    });
// 显式配置 handler,避免依赖默认值
builder.Services.AddSingleton<IHttpMessageInvoker>(sp =>
{
    var handler = new SocketsHttpHandler
    {
        MaxConnectionsPerServer = 200,
        PooledConnectionLifetime = TimeSpan.FromMinutes(5),
        PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2),
        KeepAlivePingDelay = TimeSpan.FromSeconds(30),
        KeepAlivePingTimeout = TimeSpan.FromSeconds(5),
        EnableMultipleHttp2Connections = true
    };
    return new HttpMessageInvoker(handler, disposeHandler: true);
});

注意:

HttpMessageInvoker
必须设为
Singleton
,且
disposeHandler: true
——YARP 会在应用关闭时统一释放。手动 new
HttpClient
或重复注册 handler 是最常被忽略的性能陷阱。

相关推荐