ABP VNext + Microsoft YARP:自定义反向代理与请求路由
ABP VNext + Microsoft YARP:自定义反向代理与请求路由 ☁️
📚 目录
- ABP VNext + Microsoft YARP:自定义反向代理与请求路由 ☁️
- 一、引言 🎉
- 二、环境与依赖 🛠️
- 三、项目骨架与启动配置 📁
- 3.1 目录结构
- 3.2 配置加载流程
- 3.3 Program.cs 高层结构
- 四、关键配置文件详解 ⚙️
- 五、模块化服务注册扩展 🧩
- 六、模块化中间件管道扩展 🛣️
- 6.1 请求管道流程图
- 七、熔断与重试策略(Polly)🔥
- 八、请求限流 🎯
- 九、与 ABP 认证体系深度集成 🔒
- 十、自定义中间件与扩展点 🛠️
- 十一、日志与可观测性 📈
- 十二、架构全景图 🏗️
- 十三、端到端演示与可复现脚本 🚀
- 13.1 Docker Compose
- 13.2 k6 压测脚本
- 13.3 Postman Collection
一、引言 🎉
✨ TL;DR
- 基于 ABP VNext + Microsoft YARP 构建生产级 API 网关;
- 支持动态路由热加载、熔断(Circuit Breaker)、指数退避重试⚡;
- IP/ClientID 限流、路由级授权🔒、转发头、CORS 安全策略;
- 集成 YARP Active Health Check、Prometheus 指标📊、Serilog 日志;
- 提供 Docker Compose 与 k6 脚本,一键复现🚀。
📚 背景与动机
在微服务架构中,网关负责「统一入口」「流量控制」「安全边界」「可观测性」。ABP VNext 6.x 官方推荐使用 YARP 替代 Ocelot,其高吞吐、低延迟、配置同源与可扩展性完美契合生产环境需求。
二、环境与依赖 🛠️
-
平台:.NET 6 + ABP VNext 6.x
-
关键 NuGet 包
<PackageReference Include="Microsoft.ReverseProxy" Version="1.1.0-preview.8.21451.2" /> <PackageReference Include="Polly.Extensions.Http" Version="3.0.0" /> <PackageReference Include="AspNetCoreRateLimit" Version="4.3.1" /> <PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="6.4.0" /> <PackageReference Include="prometheus-net.AspNetCore" Version="5.0.1" /> <PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
-
容器化说明
配置支持环境变量、Kubernetes ConfigMap/Secret、Docker Compose 覆盖,免重建镜像。
三、项目骨架与启动配置 📁
3.1 目录结构
Gateway/
├─ Gateway.csproj
├─ Program.cs
├─ appsettings.json
├─ appsettings.Production.json
└─ ProxyConfig/├─ Routes.json└─ Clusters.json
Services/ // 后端微服务
docker-compose.yml
3.2 配置加载流程
3.3 Program.cs 高层结构
var builder = WebApplication.CreateBuilder(args);// 1. Serilog 日志
builder.Host.UseSerilog((ctx, lc) => lc.ReadFrom.Configuration(ctx.Configuration));// 2. 配置热加载
builder.Configuration.AddJsonFile("appsettings.json", false, true).AddJsonFile("ProxyConfig/Routes.json", true, true).AddJsonFile("ProxyConfig/Clusters.json", true, true).AddEnvironmentVariables();// 3. 模块化服务注册
builder.Services.AddGatewayServices(builder.Configuration);var app = builder.Build();// 4. 模块化管道配置
app.UseGatewayPipeline();app.Run();
四、关键配置文件详解 ⚙️
// appsettings.json
{"AuthServer": {"Authority": "https://auth.mycompany.com"},"Yarp": {"Routes": [], // 从 ProxyConfig/*.json 加载"Clusters": {}},"IpRateLimiting": {"EnableEndpointRateLimiting": true,"HttpStatusCode": 429,"EndpointWhitelist": [ "/health" ],"GeneralRules": [{ "Endpoint": "/orders/*", "Period": "1s", "Limit": 5 }]},"Serilog": {"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.Elasticsearch" ],"WriteTo": [{ "Name": "Console" },{"Name": "Elasticsearch","Args": {"nodeUris": "http://elasticsearch:9200","indexFormat": "gateway-logs-{0:yyyy.MM.dd}"}}]}
}
// ProxyConfig/Routes.json
{"Routes": [{"RouteId": "orderRoute","ClusterId": "orderCluster","Match": { "Path": "/orders/{**catch-all}" },"Metadata": { "AuthorizationPolicy": "AdminOnly" }}]
}
// ProxyConfig/Clusters.json
{"Clusters": {"orderCluster": {"LoadBalancing": { "Mode": "RoundRobin" },"HealthCheck": {"Active": {"Enabled": true,"Interval": "00:00:10","Timeout": "00:00:02","Path": "/health"}},"Destinations": {"instance1": { "Address": "http://ordersvc1:80/" },"instance2": { "Address": "http://ordersvc2:80/" }}}}
}
五、模块化服务注册扩展 🧩
public static class GatewayServiceExtensions
{public static IServiceCollection AddGatewayServices(this IServiceCollection services, IConfiguration config){// 1. 转发头(真实客户端 IP)services.Configure<ForwardedHeadersOptions>(opts =>{opts.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;// 若使用 Service Mesh,可配置 KnownProxies/KnownNetworks});// 2. CORS 安全策略(白名单域)services.AddCors(opts => opts.AddPolicy("GatewayCors", p =>p.WithOrigins("https://app.mycompany.com").AllowAnyMethod().AllowAnyHeader()));// 3. 限流(In-Memory 存储)services.AddMemoryCache();services.Configure<IpRateLimitOptions>(opts => config.GetSection("IpRateLimiting").Bind(opts));services.AddInMemoryRateLimiting();services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();services.AddSingleton<IClientResolveContributor, ClaimClientIdResolveContributor>();// 4. YARP + 转发 Authorizationservices.AddReverseProxy().LoadFromConfig(config.GetSection("Yarp")).AddTransforms(t => t.AddRequestTransform(ctx =>{if (ctx.HttpContext.Request.Headers.TryGetValue("Authorization", out var token))ctx.ProxyRequest.Headers.Add("Authorization", token.ToArray());return ValueTask.CompletedTask;})).AddPolicyManager(); // Polly 策略统一注入// 5. ABP JWT 鉴权services.AddAuthentication().AddAbpJwtBearer(options =>{options.Authority = config["AuthServer:Authority"];options.Audience = "GatewayAPI";options.RequireHttpsMetadata = true;});// 6. 授权策略services.AddAuthorization(options =>options.AddPolicy("AdminOnly", p => p.RequireClaim("role", "Admin")));// 7. Prometheus HTTP 指标services.AddHttpMetrics();return services;}
}// 限流 Key:Claim client_id 或 IP
public class ClaimClientIdResolveContributor : IClientResolveContributor
{public string Name => "ClientId";public Task<RateLimitClient> ResolveClientAsync(HttpContext context){var clientId = context.User.FindFirst("client_id")?.Value?? context.Connection.RemoteIpAddress?.ToString();return Task.FromResult(new RateLimitClient { Id = clientId });}
}
六、模块化中间件管道扩展 🛣️
public static class GatewayAppExtensions
{public static IApplicationBuilder UseGatewayPipeline(this IApplicationBuilder app){// 1. Serilog 日志 — 最先捕获app.UseSerilogRequestLogging();// 2. 转发头app.UseForwardedHeaders();// 3. 路由app.UseRouting();// 4. CORSapp.UseCors("GatewayCors");// 5. 限流app.UseIpRateLimiting();// 6. 鉴权 / 授权app.UseAuthentication();app.UseAuthorization();// 7. 动态路由级授权app.Use(async (context, next) =>{var endpoint = context.GetEndpoint();var metadata = endpoint?.Metadata.GetMetadata<IDictionary<string, string>>();if (metadata != null && metadata.TryGetValue("AuthorizationPolicy", out var policyName)){var authService = context.RequestServices.GetRequiredService<IAuthorizationService>();var result = await authService.AuthorizeAsync(context.User, policyName);if (!result.Succeeded){context.Response.StatusCode = StatusCodes.Status403Forbidden;await context.Response.WriteAsync("Forbidden");return;}}await next();});// 8. Prometheus HTTP 指标app.UseHttpMetrics();// 9. 端点映射app.UseEndpoints(endpoints =>{endpoints.MapMetrics(); // /metricsendpoints.MapReverseProxy(); // 从 ProxyConfig 加载所有路由endpoints.MapGet("/health", () => Results.Ok("Healthy"));});return app;}
}
6.1 请求管道流程图
七、熔断与重试策略(Polly)🔥
public static IServiceCollection AddPolicyManager(this IReverseProxyBuilder builder)
{builder.Services.AddPolicyRegistry(registry =>{registry.Add("CircuitBreaker", HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(3, TimeSpan.FromSeconds(30)));registry.Add("Retry", HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(3, retry => TimeSpan.FromSeconds(Math.Pow(2, retry))));});builder.ConfigureHttpClient((ctx, client) =>{client.AddPolicyHandlerFromRegistry("CircuitBreaker");client.AddPolicyHandlerFromRegistry("Retry");});return builder.Services;
}
- 自动恢复:YARP Active Health Check 自动探测
/health
,后台服务恢复后断路器自动闭合。
八、请求限流 🎯
- IP + ClientID:默认按 IP,
ClaimClientIdResolveContributor
支持按 OAuth ClientId 限流。 - 白名单:
EndpointWhitelist
中配置/health
。 - 扩展点:可自定义更多
IClientResolveContributor
或IRateLimitConfiguration
。
九、与 ABP 认证体系深度集成 🔒
- 统一鉴权:使用
AddAbpJwtBearer
保持与后端一致的 OIDC/IdentityServer 设置。 - 路由授权:全局
MapReverseProxy()
后,动态中间件根据 Metadata 中AuthorizationPolicy
进行精细控制。
十、自定义中间件与扩展点 🛠️
-
全链路追踪:在 YARP Transform 中注入
traceparent
HTTP 头,配合 OpenTelemetry 收集链路。 -
异常统一处理:捕获
BrokenCircuitException
、RateLimitRejectedException
,返回标准化 JSON:{ "code":"Gateway.CircuitOpen","message":"服务暂不可用,请稍后重试" }
-
配置中心适配:将
ProxyConfig
JSON 存入 Consul/Apollo,实现灰度发布与回滚。
十一、日志与可观测性 📈
- Serilog + Elasticsearch:生产环境通过
appsettings.Production.json
配置 Sink,结构化存储与查询。 - Prometheus:
/metrics
暴露 HTTP 与 YARP EventCounters(需额外配置采集),Grafana 可视化——熔断次数、限流命中率、延迟分布等。
十二、架构全景图 🏗️
十三、端到端演示与可复现脚本 🚀
13.1 Docker Compose
version: "3.8"
services:gateway:build: ./Gatewayports: ["8080:80"]volumes:- ./Gateway/ProxyConfig:/app/ProxyConfig:ro- ./Gateway/appsettings.json:/app/appsettings.json:ro- ./Gateway/appsettings.Production.json:/app/appsettings.Production.json:roenvironment:- ASPNETCORE_ENVIRONMENT=Productiondepends_on:- ordersvc1- ordersvc2ordersvc1:image: mycompany/ordersvc:latesthealthcheck:test: ["CMD", "curl", "-f", "http://localhost/health"]interval: 10stimeout: 3sordersvc2:image: mycompany/ordersvc:latesthealthcheck:test: ["CMD", "curl", "-f", "http://localhost/health"]interval: 10stimeout: 3sprometheus:image: prom/prometheusvolumes:- ./prometheus.yml:/etc/prometheus/prometheus.ymlelasticsearch:image: docker.elastic.co/elasticsearch/elasticsearch:8.5.1
13.2 k6 压测脚本
import http from 'k6/http';
import { sleep } from 'k6';export let options = {vus: 20,duration: '30s',
};export default function () {http.get('http://localhost:8080/orders/123');sleep(0.1);
}
13.3 Postman Collection
{"info": { "name": "Gateway Tests" },"item": [{"name": "获取订单-正常","request": {"method": "GET","header": [{ "key": "Authorization", "value": "Bearer {{token}}" }],"url": { "raw": "http://localhost:8080/orders/123" }}},{"name": "获取订单-限流触发","request": {"method": "GET","url": { "raw": "http://localhost:8080/orders/123" }}}]
}