ABP VNext + Ocelot API 网关:微服务统一入口与安全策略

发布于:2025-06-29 ⋅ 阅读:(16) ⋅ 点赞:(0)

🚀 ABP VNext + Ocelot API 网关:微服务统一入口与安全策略 🛡️



一、引言 📝

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)

HTTP
ServiceDiscovery
gRPC/HTTP
gRPC/HTTP
gRPC/HTTP
客户端
Ocelot API 网关
Consul/Etcd
OrderService
UserService
ProductService

🤖 核心组件

  • 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

  1. AddEnvironmentVariables(prefix: "Ocelot__") 可通过环境变量覆盖任意 ocelot.json 节点。
  2. 确保在 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 🤖 流程图

Commit & Push
Open MR
Code Review
Approve/Request Changes
Merge
CI/CD
Smoke Test
Developer
Feature Branch
GitLab/GitHub
Reviewer
Main Branch
Deployment
Prod
Done

十、服务发现与健康检查 📡

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 中间件,输出 RequestIdLatencyStatusCode


十二、Swagger/OpenAPI 聚合 🖼️

builder.Services.AddSwaggerForOcelot(builder.Configuration);
app.UseSwaggerForOcelotUI(opt => opt.PathToSwaggerGenerator = "/swagger/docs");

附录 📎

参考链接 🔗