ABP VNext + Ocelot API 网关:微服务统一入口与安全策略
🚀 ABP VNext + Ocelot API 网关:微服务统一入口与安全策略 🛡️
📚 目录
- 🚀 ABP VNext + Ocelot API 网关:微服务统一入口与安全策略 🛡️
- 一、引言 📝
- 二、环境与依赖 📦
- 三、系统架构概览 🔍
- 🤖 核心组件
- 四、安装与初始配置 ⚙️
- Program.cs 完整示例
- 五、基础路由与聚合 🔄
- 六、负载均衡策略 ⚖️
- 七、JWT 鉴权与转发 🔑
- 八、限流、熔断与降级 🚦
- 九、动态路由热加载 🔄
- 🔀 Merge Request 🤖 流程图
- 十、服务发现与健康检查 📡
- 十一、可观测性与监控 📈
- 十二、Swagger/OpenAPI 聚合 🖼️
- 附录 📎
- 参考链接 🔗
一、引言 📝
✨ TL;DR
- 🌐 利用 Ocelot 为 ABP VNext 微服务群组构建统一网关入口
- 🔄 支持路由聚合、负载均衡、JWT 鉴权、IP/速率限流、熔断与降级
- 🔄 动态路由热加载,多环境差异化配置(本地文件、环境变量、Consul/Etcd KV)
- 🔍 集成 Consul/Etcd 服务发现和健康检查,接入 OpenTelemetry + Prometheus + Jaeger 可观测性
📚 背景与动机
在微服务架构下,客户端直连各服务带来跨域、鉴权分散、接口爆炸、性能瓶颈等挑战。API 网关 🌉 统一入口后,可集中处理路由、鉴权、限流、熔断、聚合及监控,简化客户端并提升系统安全与可维护性。
💡 选型对比
特性 | Kubernetes Ingress | Ocelot 网关 |
---|---|---|
语言实现 | NGiNX/Envoy 等 C/C++ | .NET |
路由聚合 | ❌ 无原生支持 | ✅ 原生 Aggregates |
鉴权限流 | 需额外插件 | 自带 JWT、限流、QoS、Fallback 支持 |
热加载 | 重启或重部署 IngressController | ✅ 支持本地文件、环境变量、Consul/Etcd KV 热更新 |
二、环境与依赖 📦
运行平台:.NET 6.0 + ,ABP VNext 6.x +
NuGet 包:
# Ocelot 核心
dotnet add package Ocelot
# Consul 与动态配置
dotnet add package Ocelot.Provider.Consul
dotnet add package Winton.Extensions.Configuration.Consul
dotnet add package Consul
# Etcd 可选
dotnet add package Etcd.Client
# 限流
dotnet add package AspNetCoreRateLimit
# Swagger 聚合
dotnet add package SwaggerForOcelot
项目结构:
/ApiGateway├─ appsettings.json├─ ocelot.json├─ ocelot.Development.json├─ ocelot.Production.json└─ Program.cs
三、系统架构概览 🔍
🏗️ 请求流程图 (Mermaid)
🤖 核心组件
- Ocelot 网关:统一入口、路由聚合、鉴权限流、限流、熔断降级、监控
- Consul/Etcd:服务注册/发现、健康检查、KV 存储
- ABP 微服务:业务实现及
/health
健康端点 - 监控链路:OpenTelemetry → Prometheus → Grafana;Trace → Jaeger/Zipkin
四、安装与初始配置 ⚙️
# 创建项目
dotnet new webapi -n ApiGateway
cd ApiGateway
# 引入依赖
# Ocelot 核心
dotnet add package Ocelot
# 动态配置与 Consul
dotnet add package Ocelot.Provider.Consul
dotnet add package Winton.Extensions.Configuration.Consul
dotnet add package Consul
# 限流
dotnet add package AspNetCoreRateLimit
# Swagger 聚合
dotnet add package SwaggerForOcelot
Program.cs 完整示例
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;
using AspNetCoreRateLimit;
using Winton.Extensions.Configuration.Consul;var builder = WebApplication.CreateBuilder(args);
var env = builder.Environment;// ============ 配置文件加载 ============
builder.Configuration.AddJsonFile("appsettings.json", false, true).AddJsonFile("ocelot.json", false, true).AddJsonFile($"ocelot.{env.EnvironmentName}.json", true, true).AddEnvironmentVariables(prefix: "Ocelot__").AddConsul("ocelot.json", opts => {opts.ConsulConfigurationOptions = c => c.Address = new Uri(builder.Configuration["Consul:Host"]);opts.Optional = true; opts.ReloadOnChange = true;});// ============ 服务注册 ============
builder.Services.AddHealthChecks();// CORS
builder.Services.AddCors(opts =>opts.AddPolicy("GatewayCors", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod())
);// Forwarded Headers
builder.Services.Configure<ForwardedHeadersOptions>(opts => {opts.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});// Authentication & Authorization
builder.Services.AddAuthentication().AddJwtBearer("GatewayAuth", o => {o.Authority = builder.Configuration["AuthServer:Authority"];o.Audience = "gateway_api";o.RequireHttpsMetadata = true;});
builder.Services.AddAuthorization();// Rate Limiting
builder.Services.AddOptions();
builder.Services.AddMemoryCache();
builder.Services.Configure<IpRateLimitOptions>(builder.Configuration.GetSection("RateLimitOptions"));
builder.Services.AddInMemoryRateLimiting();
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();// HttpClientFactory + DelegatingHandler 🔑
builder.Services.AddTransient<JwtTokenDelegatingHandler>();
builder.Services.AddHttpClient("OcelotClient").AddHttpMessageHandler<JwtTokenDelegatingHandler>();// Swagger 聚合
builder.Services.AddSwaggerForOcelot(builder.Configuration);// Ocelot + Consul + DelegatingHandler 🔧
builder.Services.AddOcelot(builder.Configuration).AddConsul().AddConfigStoredInConsul().AddDelegatingHandler<JwtTokenDelegatingHandler>(true);var app = builder.Build();// ============ 中间件管道 ============
app.UseForwardedHeaders();
app.UseCors("GatewayCors");
app.UseAuthentication();
app.UseAuthorization();
app.UseHealthChecks("/health");// SwaggerForOcelot UI
app.UseSwaggerForOcelotUI(opt => opt.PathToSwaggerGenerator = "/swagger/docs");app.MapGet("/", () => "🚀 API Gateway is running 🚀");await app.UseOcelot();
app.Run();
💡 Tips:
AddEnvironmentVariables(prefix: "Ocelot__")
可通过环境变量覆盖任意ocelot.json
节点。- 确保在
UseOcelot()
之前调用UseForwardedHeaders()
、UseAuthentication()
、UseAuthorization()
。
五、基础路由与聚合 🔄
📄 ocelot.json 示例
{"Routes": [{"Key": "orders","DownstreamPathTemplate": "/api/orders/{everything}","DownstreamScheme": "https","DownstreamHostAndPorts": [{ "Host": "orderservice", "Port": 443 }],"UpstreamPathTemplate": "/gateway/orders/{everything}","UpstreamHttpMethod": ["GET","POST"]},{"Key": "users","DownstreamPathTemplate": "/api/users/{everything}","UpstreamPathTemplate": "/gateway/users/{everything}","UpstreamHttpMethod": ["GET","POST"]}],"Aggregates": [{"RouteKeys": ["orders","users"],"UpstreamPathTemplate": "/gateway/dashboard","UpstreamHttpMethod": ["GET"]}]
}
⚠️ Pitfall:聚合顺序由 RouteKeys
决定,避免互依。
六、负载均衡策略 ⚖️
"LoadBalancerOptions": { "Type": "RoundRobin" }
🎯 算法:RoundRobin、LeastConnection、NoLoadBalancer。
💡 Tips:在 Kubernetes 环境下,优先使用 K8s Service 做 LB,Ocelot 仅做路由。
七、JWT 鉴权与转发 🔑
public class JwtTokenDelegatingHandler : DelegatingHandler
{protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage req, CancellationToken ct){var token = req.Headers.Authorization?.Parameter;if (!string.IsNullOrEmpty(token))req.Headers.Add("Authorization", $"Bearer {token}");return await base.SendAsync(req, ct);}
}
八、限流、熔断与降级 🚦
📄 appsettings.json
"RateLimitOptions": {"EnableEndpointRateLimiting": true,"ClientWhitelist": ["127.0.0.1"],"GeneralRules": [{ "Endpoint": "*://*/gateway/*", "Period": "1s", "Limit": 5 }]
}
📄 ocelot.json
"Routes": [{"Key": "products",// ..."QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 10000 },"Fallback": {"DownstreamFallbackPath": "/api/fallback/products","DownstreamFallbackHttpMethod": ["GET"],"IsInline": false}}
]
💡 Tips:熔断后自动路由到 Fallback 接口,可返回静态数据或降级提示。
九、动态路由热加载 🔄
Consul KV:
consul kv put ocelot.json @ocelot.json
无需重启,Ocelot 自动热加载。
🔀 Merge Request 🤖 流程图
十、服务发现与健康检查 📡
builder.Services.AddSingleton<IConsulClient>(sp =>new ConsulClient(cfg => { cfg.Address = new Uri(builder.Configuration["Consul:Host"]); })
);
// .AddOcelot() 中已 AddConsul()
❤️🩹 健康检查:微服务暴露 /health
,Consul 定期探测,剔除异常实例。
十一、可观测性与监控 📈
1️⃣ Metrics (Prometheus)
app.UseOcelot().UseMetricServer();
2️⃣ Trace (OpenTelemetry + Jaeger)
builder.Services.AddOpenTelemetryTracing(tp =>tp.AddAspNetCoreInstrumentation().AddHttpClientInstrumentation().AddJaegerExporter(opts => opts.AgentHost = builder.Configuration["Jaeger:Host"])
);
📝 日志:Serilog + RequestLogging 中间件,输出 RequestId
、Latency
、StatusCode
。
十二、Swagger/OpenAPI 聚合 🖼️
builder.Services.AddSwaggerForOcelot(builder.Configuration);
app.UseSwaggerForOcelotUI(opt => opt.PathToSwaggerGenerator = "/swagger/docs");
附录 📎
参考链接 🔗
- Ocelot 官方文档
- ABP VNext 微服务指南
- prometheus-net
- OpenTelemetry .NET