🚪 API Gateway:Ocelot 與 YARP
📌 為什麼需要 API Gateway?
沒有 Gateway 時,前端需要知道每個微服務的位址:
前端直接呼叫多個微服務(❌ 不好的做法):
├── http://user-service:5001/api/users
├── http://product-service:5002/api/products
├── http://order-service:5003/api/orders
└── http://payment-service:5004/api/payments
有 Gateway 後,前端只需要知道一個位址:
前端只呼叫 Gateway(✅ 推薦做法):
└── http://api-gateway:5000/api/...
├── /api/users → 轉發到 user-service
├── /api/products → 轉發到 product-service
├── /api/orders → 轉發到 order-service
└── /api/payments → 轉發到 payment-service
API Gateway 的功能
| 功能 | 說明 |
|---|---|
| 路由 | 根據 URL 轉發到對應的微服務 |
| 負載平衡 | 將請求分散到多個服務實例 |
| 認證授權 | 統一在 Gateway 驗證 JWT Token |
| 速率限制 | 防止 API 被濫用 |
| 快取 | 快取常用的回應 |
| 聚合 | 合併多個服務的回應為一個 |
| 日誌 | 集中記錄所有 API 請求 |
📌 Ocelot 設定與路由
安裝與設定
dotnet new webapi -n ApiGateway
cd ApiGateway
dotnet add package Ocelot
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
builder.Services.AddOcelot();
var app = builder.Build();
await app.UseOcelot();
app.Run();
ocelot.json 路由設定
{
"Routes": [
{
"DownstreamPathTemplate": "/api/products/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{ "Host": "product-service", "Port": 5002 }
],
"UpstreamPathTemplate": "/api/products/{everything}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ]
},
{
"DownstreamPathTemplate": "/api/orders/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{ "Host": "order-service", "Port": 5003 }
],
"UpstreamPathTemplate": "/api/orders/{everything}",
"UpstreamHttpMethod": [ "GET", "POST" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer"
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000"
}
}
📌 YARP — 微軟官方反向代理
YARP(Yet Another Reverse Proxy)是微軟官方的高效能反向代理,比 Ocelot 更適合大規模生產環境。
安裝
dotnet add package Yarp.ReverseProxy
設定 YARP
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapReverseProxy();
app.Run();
appsettings.json 設定
{
"ReverseProxy": {
"Routes": {
"product-route": {
"ClusterId": "product-cluster",
"Match": {
"Path": "/api/products/{**catch-all}"
},
"Transforms": [
{ "PathPattern": "/api/products/{**catch-all}" }
]
},
"order-route": {
"ClusterId": "order-cluster",
"Match": {
"Path": "/api/orders/{**catch-all}"
}
}
},
"Clusters": {
"product-cluster": {
"LoadBalancingPolicy": "RoundRobin",
"Destinations": {
"product-1": { "Address": "http://product-service-1:5002" },
"product-2": { "Address": "http://product-service-2:5002" }
}
},
"order-cluster": {
"Destinations": {
"order-1": { "Address": "http://order-service:5003" }
}
}
}
}
}
📌 Ocelot vs YARP 比較
| 功能 | Ocelot | YARP |
|---|---|---|
| 維護者 | 社群 | 微軟官方 |
| 效能 | 中 | 高 |
| 設定方式 | JSON 檔案 | JSON / 程式碼 |
| 負載平衡 | 內建 | 內建(更多策略) |
| 熱更新設定 | 支援 | 支援 |
| 自訂中介軟體 | 有限 | 靈活 |
| 適用場景 | 中小型專案 | 大型生產環境 |
📌 負載平衡與速率限制
YARP 負載平衡策略
// 支援的負載平衡策略
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
// 可用的策略:
// - RoundRobin:輪流分配
// - Random:隨機
// - LeastRequests:最少請求
// - PowerOfTwoChoices:隨機選兩個,選負載低的
// - FirstAlphabetical:按字母順序(測試用)
速率限制
// 使用 ASP.NET Core 內建的速率限制
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("fixed", opt =>
{
opt.PermitLimit = 100; // 每個窗口 100 個請求
opt.Window = TimeSpan.FromMinutes(1);
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 10;
});
options.AddSlidingWindowLimiter("sliding", opt =>
{
opt.PermitLimit = 60;
opt.Window = TimeSpan.FromMinutes(1);
opt.SegmentsPerWindow = 6; // 每 10 秒一個段落
});
});
app.UseRateLimiter();
📌 認證與授權在 Gateway 層
// Program.cs — API Gateway
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "http://identity-service";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("admin"));
});
// 在 YARP 路由中套用授權
// appsettings.json 中設定 AuthorizationPolicy
📌 範例:聚合多個服務的回應
// 自訂聚合端點:一次呼叫取得訂單 + 產品 + 用戶資訊
app.MapGet("/api/aggregation/order-details/{orderId}",
async (Guid orderId, IHttpClientFactory factory) =>
{
var orderClient = factory.CreateClient("OrderService");
var productClient = factory.CreateClient("ProductService");
var userClient = factory.CreateClient("UserService");
// 平行呼叫三個服務
var orderTask = orderClient.GetFromJsonAsync<OrderDto>(
$"/api/orders/{orderId}");
var order = await orderTask;
if (order is null) return Results.NotFound();
var userTask = userClient.GetFromJsonAsync<UserDto>(
$"/api/users/{order.CustomerId}");
var productTasks = order.Items.Select(item =>
productClient.GetFromJsonAsync<ProductDto>(
$"/api/products/{item.ProductId}"));
await Task.WhenAll(userTask, Task.WhenAll(productTasks));
return Results.Ok(new
{
Order = order,
Customer = userTask.Result,
Products = productTasks.Select(t => t.Result)
});
});
下一章: 我們將學習如何用 Docker 容器化微服務。