前言:YARP 与 .NET 10 的相遇
YARP(Yet Another Reverse Proxy)最初源于微软内部的一个现实困境:大量内部团队在各自构建反向代理,重复造轮子。微软决定将这些需求合并,打造一个统一的高性能反向代理库。
如今,YARP 已经过规模化验证,微软每天在生产中使用它处理数十亿次请求。在 .NET 10 时代,YARP 作为 NuGet 包提供,可运行于 Windows、Linux 和 macOS,并已被正式纳入 ASP.NET Core 文档体系。
整个接入过程只需三行代码:
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));// ...app.MapReverseProxy();AddReverseProxy()负责向依赖注入容器注册 YARP 所需的全部服务,.LoadFromConfig()从appsettings.json加载路由和集群配置,app.MapReverseProxy()则将 YARP 中间件接入请求处理管道。
但这三行背后究竟发生了什么?让我们从源码出发,逐层拆解。
一、AddReverseProxy()的本质:一个精心设计的 DI 编排器
打开 GitHub 上dotnet/yarp仓库的ReverseProxyServiceCollectionExtensions.cs,AddReverseProxy()的完整实现如下:
publicstaticIReverseProxyBuilderAddReverseProxy(thisIServiceCollectionservices){varbuilder=newReverseProxyBuilder(services);builder.AddConfigBuilder().AddRuntimeStateManagers().AddConfigManager().AddSessionAffinityPolicies().AddActiveHealthChecks().AddPassiveHealthCheck().AddLoadBalancingPolicies().AddDestinationResolver().AddProxy();if(OperatingSystem.IsWindows()){builder.AddHttpSysDelegation();}else{builder.Services.TryAddSingleton<IHttpSysDelegator,DummyHttpSysDelegator>();}services.TryAddSingleton<ProxyEndpointFactory>();services.AddDataProtection();services.AddAuthorization();services.AddCors();services.AddRouting();returnbuilder;}这个方法做了两件事:第一,构建并返回IReverseProxyBuilder(一个流式 API 的构建器),第二,通过九个内部子方法将整个代理系统所需的服务注入 DI 容器。
返回值的意义不可忽视。AddReverseProxy()返回的是IReverseProxyBuilder而不是IServiceCollection,这打破了常规的AddXxx()模式。这是有意为之的设计——它将 YARP 的扩展点从通用的服务集合中隔离出来,给出一个专属的配置命名空间。所有后续的链式调用,比如.LoadFromConfig()、.AddTransforms()、.ConfigureHttpClient(),都是IReverseProxyBuilder的扩展方法,而不是IServiceCollection的。
二、九个子系统的逐一拆解
2.1AddConfigBuilder()— 配置解析层
这是路由配置的翻译器。它注册的核心服务负责将用户在appsettings.json或代码中定义的路由规则,解析并编译成 YARP 内部使用的高效数据结构。具体来说,它处理路由匹配规则(Path、Headers、Methods 等的组合条件)以及将RouteConfig转化为 ASP.NET Core 的Endpoint。
2.2AddRuntimeStateManagers()— 运行时状态层
YARP 的路由和集群状态都存储在内存中,由运行时状态管理器维护。这一层注册的是IProxyStateLookup、RouteState、ClusterState、DestinationState等运行时对象的管理者。每一个集群下的目标节点(DestinationState)都携带健康状态、当前并发连接数等实时信息,负载均衡算法基于这些数据做决策。
2.3AddConfigManager()— 配置热重载层
这是 YARP 最受欢迎的特性之一:无需重启应用即可更新路由配置。IProxyConfigManager订阅IProxyConfigProvider的变更通知(通过IChangeToken机制,与IConfiguration的热重载体系打通),当配置发生变化时,它会原子性地替换内存中的路由和集群状态,整个过程对进行中的请求是透明的。
2.4 策略三件套:AddSessionAffinityPolicies()/AddActiveHealthChecks()/AddPassiveHealthCheck()
会话亲和(Session Affinity)确保同一客户端的请求始终路由到同一后端实例,对需要本地状态的服务(如 WebSocket 长连接、Server-Sent Events)至关重要。YARP 内置基于 Cookie 和基于自定义请求头的两种亲和策略。
主动健康检查(Active Health Check)由后台定时任务发起,周期性地向每个目标的健康端点发送 HTTP 探针。如下配置启用了 10 秒一次的探测,连续失败则标记该目标为不健康:
"HealthCheck":{"Active":{"Enabled":true,"Interval":"00:00:10","Timeout":"00:00:05","Policy":"ConsecutiveFailures"}}被动健康检查(Passive Health Check)则更为轻量,它监听实际代理请求的结果:当某个目标持续返回 5xx 或连接超时,YARP 会将其临时标记为不健康,并在ReactivationPeriod后重新尝试。这两种机制共同构成 YARP 的全面健康监控体系,确保流量只被路由到健康的目标实例。
2.5AddLoadBalancingPolicies()— 负载均衡层
负载均衡策略通过AddLoadBalancingPolicies()在 DI 容器中注册,并由UseLoadBalancing()中间件在MapReverseProxy()的默认管道中激活。如果没有指定策略,默认使用PowerOfTwoChoices。
内置策略包括:
| 策略名 | 算法描述 |
|---|---|
RoundRobin | 轮询,均匀分发 |
Random | 随机选择 |
LeastRequests | 选择当前活跃请求数最少的目标 |
PowerOfTwoChoices | 随机选两个,取负载较低者(默认) |
FirstAlphabetical | 按名称排序,优先第一个(金丝雀发布场景) |
你也可以实现ILoadBalancingPolicy接口来注入自定义策略,通过PickDestination方法完全控制目标选择逻辑。
2.6AddDestinationResolver()与AddProxy()— 核心转发层
AddDestinationResolver()注册目标地址解析器。默认情况下目标地址是静态配置的,但通过.AddDnsDestinationResolver()可以启用 DNS 动态解析,适用于目标 IP 会变化的 Kubernetes 场景。
AddProxy()是最核心的一步,它注册了IHttpForwarder(YARP 的 HTTP 转发引擎)和ITransformBuilder(请求/响应变换系统)。HttpForwarder基于SocketsHttpHandler构建,支持 HTTP/1.1、HTTP/2 和 HTTP/3,并针对高并发进行了连接池优化。
三、IReverseProxyBuilder链式 API 的扩展点全景
3.1 配置加载:两种模式
声明式(最常见):
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));LoadFromConfig()注册了ConfigurationConfigProvider作为IProxyConfigProvider的单例实现,通过闭包捕获IConfiguration对象,并支持配置变更通知。
编程式(精确控制):
builder.Services.AddReverseProxy().LoadFromMemory(routes:new[]{newRouteConfig{RouteId="api-route",ClusterId="api-cluster",Match=newRouteMatch{Path="/api/{**catch-all}"}}},clusters:new[]{newClusterConfig{ClusterId="api-cluster",Destinations=newDictionary<string,DestinationConfig>{["node1"]=new(){Address="https://api1.internal/"},["node2"]=new(){Address="https://api2.internal/"}}}});更高级的场景可以实现IProxyConfigProvider接口,从数据库、服务注册中心(Consul、etcd)或 Kubernetes API Server 动态拉取配置,实现真正的服务发现集成。
3.2 请求变换:AddTransforms()
变换(Transforms)是 YARP 中修改请求和响应的标准机制:
builder.Services.AddReverseProxy().LoadFromConfig(...).AddTransforms(ctx=>{// 全局添加 X-Forwarded-For 请求头ctx.AddXForwardedFor();// 对特定集群的请求注入自定义头if(ctx.Cluster?.ClusterId=="internal-cluster"){ctx.AddRequestHeader("X-Internal-Token","secret",append:false);}// 自定义响应头处理ctx.AddResponseHeaderRemove("Server");});AddTransforms()可被多次调用,每个回调独立注册为ITransformProvider的单例,YARP 在处理每个路由时按注册顺序依次执行所有变换。对于更复杂的场景,可以实现ITransformFactory来根据路由配置中的元数据动态生成变换逻辑。
3.3 自定义 HTTP 客户端:ConfigureHttpClient()
builder.Services.AddReverseProxy().LoadFromConfig(...).ConfigureHttpClient((ctx,handler)=>{// 为特定集群禁用 SSL 验证(仅限内网场景)if(ctx.NewConfig.ClusterId=="dev-cluster"){handler.SslOptions.RemoteCertificateValidationCallback=(_,_,_,_)=>true;}// 调整连接池参数handler.PooledConnectionLifetime=TimeSpan.FromMinutes(5);handler.MaxConnectionsPerServer=100;});3.4 配置过滤器:AddConfigFilter<T>()
可通过AddConfigFilter<T>()注册多个IProxyConfigFilter过滤器,它们按注册顺序运行,可以在配置生效前对路由和集群进行动态修改或验证。这是实现多租户路由、基于环境变量动态调整目标地址等场景的利器。
四、app.MapReverseProxy()— 中间件管道的激活
AddReverseProxy()只是注册服务,真正让代理"运转"的是app.MapReverseProxy()。它将 YARP 的中间件管道注册为 ASP.NET Core 路由系统中的一组Endpoint。
默认管道包含以下中间件(按顺序):
请求到达 ↓ SessionAffinity // 会话亲和性检查 ↓ LoadBalancing // 选择目标节点 ↓ PassiveHealthCheck // 监测响应结果更新健康状态 ↓ HttpForwarder // 实际 HTTP 转发 ↓ 响应返回你可以通过 lambda 参数完全自定义这条管道,在任意位置插入自己的中间件:
app.MapReverseProxy(proxyPipeline=>{// 在转发前执行鉴权逻辑proxyPipeline.Use(async(context,next)=>{varroute=context.GetReverseProxyFeature().Route;// 根据路由元数据决定是否需要鉴权if(route.Config.Metadata?.ContainsKey("RequireAuth")==true){if(context.User.Identity?.IsAuthenticated!=true){context.Response.StatusCode=401;return;}}awaitnext();});// 仍然启用默认的负载均衡proxyPipeline.UseLoadBalancing();});五、配置模型深度解析:Routes 与 Clusters
YARP 的配置围绕两个核心概念构建。Routes(路由)定义匹配规则,Clusters(集群)定义后端目标集合,它们通过ClusterId关联。
一个完整的生产级配置示例:
{"ReverseProxy":{"Routes":{"api-v2-route":{"ClusterId":"api-cluster","Match":{"Path":"/api/v2/{**catch-all}","Headers":[{"Name":"X-API-Version","Values":["2"],"Mode":"ExactHeader"}]},"Transforms":[{"PathPattern":"/v2/{**catch-all}"},{"RequestHeader":"X-Forwarded-Host","Append":"{host}"}],"Metadata":{"RequireAuth":"true"}}},"Clusters":{"api-cluster":{"LoadBalancingPolicy":"LeastRequests","SessionAffinity":{"Enabled":true,"Policy":"Cookie","AffinityKeyName":"YARP-AFFINITY"},"HealthCheck":{"Active":{"Enabled":true,"Interval":"00:00:15","Timeout":"00:00:05","Policy":"ConsecutiveFailures","Path":"/healthz"},"Passive":{"Enabled":true,"Policy":"TransportFailureRate","ReactivationPeriod":"00:02:00"}},"HttpClient":{"MaxConnectionsPerServer":50,"DangerousAcceptAnyServerCertificate":false},"Destinations":{"api-1":{"Address":"https://api-node1.internal:8080/","Health":"https://api-node1.internal:8080/healthz"},"api-2":{"Address":"https://api-node2.internal:8080/","Health":"https://api-node2.internal:8080/healthz"}}}}}}六、与 ASP.NET Core 生态的深度集成
AddReverseProxy()在最后调用了services.AddDataProtection()、services.AddAuthorization()、services.AddCors()和services.AddRouting()。这不是随意为之。
AddAuthorization()使 YARP 路由可以直接配置AuthorizationPolicy,借用 ASP.NET Core 的授权中间件保护代理端点,无需重复实现。AddCors()允许在代理层统一处理 CORS 预检请求,避免后端服务各自处理的复杂性。AddDataProtection()为会话亲和的 Cookie 提供加密和篡改防护。AddRouting()将 YARP 的路由规则整合进 ASP.NET Core 的端点路由系统,可以与 Controller、Minimal API、SignalR 等端点共存同一个应用中,互不干扰。
与 JWT 鉴权的集成只需在AddReverseProxy()之前配置:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options=>{options.Authority="https://identity.example.com";options.Audience="proxy-api";});builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));// ...app.UseAuthentication();app.UseAuthorization();app.MapReverseProxy();七、生产实践建议
可观测性优先。YARP 与 .NET 的ILogger、OpenTelemetry 和EventCounters深度集成。在生产环境中,应至少配置对Yarp.ReverseProxy命名空间的结构化日志,并通过 Prometheus 采集 YARP 暴露的性能计数器(活跃请求数、转发成功率、P99 延迟等)。
合理利用{**catch-all}通配符。{**catch-all}模式使 YARP 能够转发所有子路径,非常适合整段 API 路由的代理场景。但需注意,过于宽泛的通配符可能意外捕获不希望代理的端点(如/healthz或/metrics),应通过路由优先级或更精确的路径匹配来规避。
HttpClient 连接池调优。对于高并发场景,SocketsHttpHandler的默认参数往往不够用。MaxConnectionsPerServer默认是无限制,PooledConnectionIdleTimeout默认 2 分钟,这在突发流量场景下容易导致连接数暴涨。建议根据实际 QPS 和后端容量显式配置这些参数。
配置过滤器用于动态路由。在微服务场景中,服务实例动态增减是常态。实现IProxyConfigProvider对接服务注册中心,配合IChangeToken的变更通知,可以在不重启应用的情况下实时更新路由表,达到真正的零停机服务发现。
结语
builder.Services.AddReverseProxy()是一行代码,但它背后是九个子系统的精密编排:配置解析、状态管理、热重载、会话亲和、双模式健康检查、多策略负载均衡、目标解析以及基于SocketsHttpHandler的高性能 HTTP 转发引擎。
YARP 的设计哲学是**“库而非框架”**——它被设计为可轻松定制和调整以满足每种部署场景特定需求的工具包,而不是要求你通过脚本或重新编译来扩展的封闭系统。这正是它在微软内部、以及越来越多的生产系统中成为 API 网关首选的根本原因。
对于 .NET 10 开发者而言,YARP 已经不是"可选项",而是构建现代微服务架构时应当优先考虑的基础设施选择。