网关反向代理,在 .NET 生态里不一定要用 Nginx 或第三方中间件。微软开源的 YARP 让你直接用 ASP.NET Core 代码搭一个,路由、负载均衡、动态配置全在 C# 层面解决,和现有认证中间件天然打通。这篇文章不讲空话,直接说怎么用、为什么用、什么场景别用。
YARP 是什么
YARP(Yet Another Reverse Proxy)是微软开源的一款 .NET 反向代理库。它不是一个开箱即用的独立网关服务,而是一个 NuGet 包——你把它装进 ASP.NET Core 项目里,你的项目就成了反向代理。
官网:
https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/servers/yarp/getting-started?view=aspnetcore-10.0
GitHub:
https://github.com/microsoft/reverse-proxy
YARP 解决的核心问题是:在 .NET 体系内用代码而非配置文件来构建反向代理。传统上做反向代理你会想到 Nginx、HAProxy,它们用声明式配置做事;YARP 的路线是让你用 C# 代码来定义路由、负载均衡、中间件行为。这意味着你可以把代理逻辑和业务逻辑写在一起、动态调整路由、复用已有的认证授权中间件——这些东西用 Nginx 做起来会相当别扭。
YARP 的核心机制包括:
路由与集群:
通过 Routes定义匹配规则(路径、主机、Header),通过Clusters定义上游服务集群。负载均衡:
内置 RoundRobin、Random、LeastRequests、PowerOfTwoChoices(默认)等策略。其中 PowerOfTwoChoices是超高并发场景的优选,它随机选两个节点并从中挑选请求数最少的,在避免全局锁的同时近似达到LeastRequests的效果。请求转换:
支持对转发前的请求进行路径重写、Header 增删改等转换操作。 中间件管道:
YARP 本身是 ASP.NET Core 中间件,可以跟认证、授权、日志、限流等中间件自由组合。 可观测性:
原生支持 OpenTelemetry 分布式追踪。
YARP 在 .NET 基础架构之上实现,可在 Windows、Linux 或 macOS 上运行。微软自家的 Azure 应用服务已经用上了 YARP——这算是官方背书级别的生产验证。
怎么引入
引入 YARP 只需要安装一个 NuGet 包。
安装包
安装对应 NuGet 包即可。
最小接入配置(Program.cs)
这段代码做了两件事:AddReverseProxy() 把 YARP 的核心服务注册到依赖注入容器;MapReverseProxy() 把反向代理中间件挂到请求管道上。
配置文件(appsettings.json 最小示例)
Routes 定义请求匹配规则,Clusters 定义转发目标。上面这个配置把所有 /api/ 开头的请求转发到 https://localhost:7001/。
可选项
需要负载均衡:在 Clusters下配置LoadBalancingPolicy。需要路径重写:在 Routes下配置Transforms。需要代码级动态配置:实现 IProxyConfigProvider替代LoadFromConfig。
验证接入是否成功
启动项目后,访问一个匹配路由的路径(如 /api/xxx),如果请求被正确转发到目标地址并返回响应,说明接入成功。也可以查看日志输出,YARP 会记录路由匹配和转发信息。
快速上手
示例一:最小可用反向代理
这就是上面“怎么引入”里的最小配置——一个空 ASP.NET Core 项目加 YARP,把所有请求转发到同一个上游服务。这是最基础的用法,5 分钟跑通。
启动一个上游服务(比如另一个 WebAPI 项目跑在 https://localhost:7001),再启动 YARP 代理项目(跑在 https://localhost:5000)。访问 https://localhost:5000/api/xxx,实际会转发到 https://localhost:7001/api/xxx。
示例二:多路由分发(微服务网关场景)
配置多个路由,把不同路径的请求分发到不同的后端服务:
/order/ 开头的请求走订单服务,/user/ 开头的走用户服务。这就是一个最基础的微服务网关雏形。
示例三:负载均衡(多实例部署)
在一个集群下配置多个目标地址,并指定负载均衡策略:
可用策略包括 RoundRobin、Random、LeastRequests、PowerOfTwoChoices。生产环境如果后端实例性能差异较大,可以考虑 LeastRequests,它倾向于把请求分给当前活跃请求数最少的节点。
示例四:路径重写(Transforms)
很多时候后端服务的路由前缀和网关暴露的不一致,需要用 Transforms 做路径转换。比如网关暴露 /api/order/create,但后端真正的路径是 /order-service/v1/create。
{**remainder} 会捕获通配符部分,然后拼接到新的路径模板里。注意 Transforms 中用于重写路径的键名是 PathRouteValuesPattern,而非 PathPattern。
边界提醒: 当用户请求为 /api/order/(末尾带斜杠)时,{**remainder} 捕获的值为空字符串,拼接后目标路径为 /order-service/v1/。后端服务能否正确处理这个路径取决于其路由设计。如果后端不兼容空后缀,可能返回 404。生产环境可以考虑使用 PathRemovePrefix 转换器做更稳定的前缀裁剪,或者在目标路径中显式处理斜杠边界。Transforms 还支持修改 Header、查询字符串、主机名等,能满足多数网关的 URL 重写需求。
示例五:代码级动态路由(IProxyConfigProvider)
如果路由信息存储在数据库或配置中心,需要在运行时动态更新,可以实现 IProxyConfigProvider 接口。YARP 会自动检测并使用容器中已注册的 IProxyConfigProvider,无需额外调用 LoadFromMemory 等不存在的扩展方法。
下面是一个完整的、可直接编译的实现,包含 IProxyConfig 的显式实现和 CancellationChangeToken 信号触发机制:
这种方式让你的网关具备动态配置能力,无需重启即可调整路由或上下线后端节点。需要注意:每次刷新时必须利用新的 CancellationTokenSource 生成 CancellationChangeToken 并赋给 IProxyConfig.ChangeToken,YARP 才能感知配置变更。
常见报错排查
1. 404 Not Found
触发条件:请求路径没有匹配到任何 Route。 报错信息: No route match found for the current request.修复方式:检查 Route 的 Match.Path是否写对,注意{**catch-all}通配符能匹配到子路径,不带通配符的精确匹配则不会匹配子路径。如果使用动态配置,确认LoadRoutes()返回了有效的路由列表。
2. 连接失败(Connection refused)
触发条件:Cluster 中配置的 Destination 地址不可达(服务未启动或端口错误)。 报错信息: System.Net.Http.HttpRequestException: Connection refused修复方式:确认上游服务是否正常运行,检查 Address 的协议(http/https)和端口是否一致。本地调试时特别注意自签名证书问题,可能需要配置 HttpClient跳过证书验证(仅在非生产环境使用)。
3. 502 Bad Gateway(超时)
触发条件:上游服务响应时间超过 YARP 默认的 100 秒超时限制,或后端处理时间过长。 报错信息: The request timed out.修复方式:在 Cluster 配置中调整 HttpClient.Timeout属性,或者优化上游接口性能。
适用场景
微服务网关是 YARP 最典型的应用场景。开发团队规模中等以上、拆分出多个业务域服务时,用 YARP 做统一的流量入口可以集中处理认证鉴权、跨域、限流、日志聚合等横切关注点。由于 YARP 本身就运行在 ASP.NET Core 管道上,你可以直接复用标准的 JWT 认证中间件,并通过在路由配置中指定 "AuthorizationPolicy" 来对接 ASP.NET Core 的策略授权,比在 Nginx 里写 Lua 脚本要自然得多。
API 聚合与协议转换也是合适的场景。YARP 支持修改请求和响应,虽然它本身不是专门的聚合层框架,但结合 .NET 的 HttpClientFactory 和自定义中间件,可以实现轻量级的请求合并和适配。
A/B 测试与灰度发布用得着它。通过代码动态路由可以根据 Header、Cookie 甚至用户 ID 将流量按比例或规则分配到不同版本的服务集群,配置在代码层做比静态配置文件灵活不少。
不太适合的场景包括:纯静态文件反向代理(Nginx 效率更高)、四层 TCP/UDP 代理(YARP 只支持 HTTP/HTTPS 和 WebSocket)、极低延迟要求的转发(YARP 的 .NET 托管开销会比 Nginx 的 C 实现稍高)。另外,如果你团队没有 .NET 开发能力,或者运维体系完全依赖 K8s Ingress 标准,引入 YARP 会增加维护成本,这种情况用云原生方案更省事。
实战建议
高频坑位一:转发请求头丢失
YARP 默认会转发大部分标准请求头,但自定义 Header 可能被过滤或未正确传递。如果后端依赖特定自定义头(如 X-User-Id),建议在 Route 的 Transforms 中显式设置或通过自定义中间件强制添加,不要依赖默认转发行为。
触发条件:使用了自定义 Header 且未在 YARP 端显式声明。
错误表现:后端接收不到网关透传的用户上下文信息。
修复策略:在 AddReverseProxy() 时自定义 IHttpClientFactory 或在 Transforms 中配置 RequestHeader 操作。
高频坑位二:负载均衡状态下某个实例异常仍继续被转发
YARP 的被动健康检查默认只标记失败,不会快速熔断异常实例。如果某个上游实例挂掉,YARP 不会立即将其剔除出负载均衡池,导致部分请求持续报 502。
触发条件:上游实例崩溃但进程仍在(如端口监听但请求挂起),或实例下线但未从 Destination 列表中移除。
错误表现:请求频繁出现超时或 5xx 错误,且不均衡。
修复策略:配置主动健康检查,定期探测后端实例的健康状态,异常时自动从可用列表摘除。配置示例(注意必须指定 Policy,通常使用 ConsecutiveFailures):
高频坑位三:动态配置变更引发内存泄漏
使用自定义 IProxyConfigProvider 时,如果频繁触发配置刷新,但未妥善管理 CancellationToken 的生命周期,或者每次刷新都全量重建大量规则对象,会导致 HttpContext 管道频繁重建,引起内存碎片和 GC 压力增加。
触发条件:高频率调用 ChangeToken 的 Signal 触发刷新,但旧的 CancellationTokenSource 未被及时 Dispose;或者每次刷新都创建全新的路由/集群对象且未复用不变的部分。
错误表现:内存持续上涨,GC 频繁,甚至 OOM。
修复策略:确保每次刷新时,利用新的 CancellationTokenSource 生成 CancellationChangeToken 并赋给 IProxyConfig.ChangeToken,同时及时 Dispose 旧的 CancellationTokenSource(如示例五所示)。降低刷新频率,仅在配置真正变更时触发信号。同时监控 GC 指标,必要时手动调用 GC.Collect() 仅用于排查,生产环境不建议。
其他推荐实践
集成 OpenTelemetry:
YARP 内置了对 Distributed Tracing 的支持,开启后可以在 Jaeger 或 Zipkin 中看到完整的代理链路,排查问题事半功倍。 合理设置超时:
Clusters 级别可配置 HttpClient.Timeout,建议结合上游平均响应时间设置,不要太长(避免线程堆积)也不要太短(避免误判超时)。与限流中间件配合:
YARP 本身不内置限流,但可以搭配 .NET 8 的 RateLimiting中间件使用,在 YARP 路由匹配之前或之后生效都可以。
选型建议
什么场景相对更适合选 YARP:
你所在团队已经是 .NET 技术栈,不想额外引入 Nginx 或 Traefik 等外部组件。 需要频繁变更路由规则,且希望用代码逻辑控制流量分发(如基于灰度标签、租户 ID)。 网关层需要和现有 .NET 认证授权(如 IdentityServer、JWT)深度集成,不想在两层代理中维护两套配置。 并发量在每秒数千到数万级别,且后续可用 .NET 性能优化手段(如 Kestrel 调优、HTTP/3)做垂直扩展。
什么场景可能不太适合:
纯静态资源服务或超高并发(单机 10 万+ QPS)场景,Nginx/OpenResty 或云厂商的 CDN 网关会更有优势。 团队运维能力有限,完全依赖 K8s Ingress Controller 且不愿额外维护 .NET 网关进程。 主要需求是四层代理或 UDP 透传,YARP 不支持这类协议。 上线周期紧迫,团队对 .NET 8 新特性尚不熟悉,建议先从更成熟的 Ocelot 过渡,或直接用云服务商的全托管网关。
综合判断:如果你已经决定用 .NET 8 作为主技术栈,且需要构建微服务网关或边车代理,YARP 值得优先考虑。它比 Ocelot 更轻量、性能更好(基于原生 HttpClient 和 Kestrel),且有微软持续维护。如果并发预估在 2 万 QPS 以上,务必提前做压测,根据结果调整 System.Threading.ThreadPool 参数和连接池限制,同时做好水平扩展的多节点部署。
总结收尾
你不需要在微服务网关选型上反复纠结。如果团队已经在 .NET 生态里,YARP 是构建反向代理最顺手的那把刀——配置比 Nginx 灵活,性能比 Ocelot 强,还能跟你的业务代码长在一起。从最小可用配置起步,先把流量转起来,再逐步把负载均衡、健康检查、动态路由加进去。不要一上来就追求全量动态配置,先把文件配置跑稳定,哪天发现配置文件改不动了,再把 IProxyConfigProvider 拿出来。建议今天拿一个空闲的 WebAPI 项目试一下 YARP 的转发效果,确认它能跑通再考虑替换现有的网关组件。