1. Tổng quan và chuẩn bị môi trường
Để xây dựng một hệ thống Microservices mạnh mẽ, API Gateway đóng vai trò là cửa ngõ duy nhất tiếp nhận và điều phối các yêu cầu từ phía client. Ocelot là một framework mã nguồn mở phổ biến trong hệ sinh thái .NET, hỗ trợ đắc lực cho việc định tuyến, xác thực và quản lý lưu lượng.
Các thư viện cần thiết (NuGet Packages):
- Ocelot
- Ocelot.Provider.Consul (Phát hiện dịch vụ)
- Ocelot.Provider.Polly (Khả năng chịu lỗi)
- Ocelot.Cache.CacheManager (Quản lý bộ đệm)
- IdentityServer4.AccessTokenValidation (Xác thực)
2. Cấu hình Ocelot cơ bản
Quy trình tích hợp Ocelot vào dự án WebAPI bao gồm việc thiết lập tệp cấu hình JSON và đăng ký dịch vụ trong mã nguồn ứng dụng.
Cấu hình trong Startup.cs
Tại phương thức ConfigureServices, thực hiện đăng ký Ocelot:
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot()
.AddConsul()
.AddPolly();
}
Tại phương thức Configure, kích hoạt Middleware của Ocelot trước các định nghĩa định tuyến khác:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseOcelot().Wait();
}
Định nghĩa tệp cấu hình gateway.json
Tệp cấu hình này chứa các quy tắc định tuyến từ Upstream (Client) đến Downstream (Microservices):
{
"Routes": [
{
"DownstreamPathTemplate": "/api/{catchAll}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"UpstreamPathTemplate": "/gateway/{catchAll}",
"UpstreamHttpMethod": [ "Get", "Post" ],
"LoadBalancerOptions": {
"Type": "RoundRobin"
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000"
}
}
3. Tích hợp Service Discovery với Consul
Khi số lượng dịch vụ tăng lên, việc cấu hình cứng địa chỉ IP là không khả thi. Việc kết hợp với Consul giúp Ocelot tự động tìm thấy các instance khả dụng.
{
"Routes": [
{
"DownstreamPathTemplate": "/api/{everything}",
"DownstreamScheme": "https",
"UpstreamPathTemplate": "/service-a/{everything}",
"UpstreamHttpMethod": [ "Get" ],
"ServiceName": "ProductService",
"UseServiceDiscovery": true,
"LoadBalancerOptions": {
"Type": "LeastConnection"
}
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Host": "127.0.0.1",
"Port": 8500,
"Type": "Consul"
}
}
}
4. Tùy chỉnh chiến lược cân bằng tải (Load Balancing)
Ngoài các thuật toán mặc định như RoundRobin hay LeastConnection, chúng ta có thể tự định nghĩa chiến lược riêng bằng cách kế thừa giao diện ILoadBalancer.
public class CustomTrafficBalancer : ILoadBalancer
{
private readonly Func<Task<List<Service>>> _fetchServices;
private readonly object _syncLock = new object();
public CustomTrafficBalancer(Func<Task<List<Service>>> fetchServices)
{
_fetchServices = fetchServices;
}
public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
{
var availableNodes = await _fetchServices();
if (availableNodes == null || availableNodes.Count == 0)
{
return new ErrorResponse<ServiceHostAndPort>(new ServicesAreEmptyError("Không có dịch vụ khả dụng"));
}
lock (_syncLock)
{
// Logic chọn ngẫu nhiên một node
var randomPtr = new Random().Next(availableNodes.Count);
var target = availableNodes[randomPtr];
return new OkResponse<ServiceHostAndPort>(target.HostAndPort);
}
}
public void Release(ServiceHostAndPort hostAndPort) { }
}
Đăng ký chiến lược tùy chỉnh trong Startup:
services.AddOcelot().AddCustomLoadBalancer<CustomTrafficBalancer>();
5. Quản lý bộ đệm và khả năng chịu lỗi
Cấu hình Caching
Sử dụng bộ đệm giúp giảm tải cho các dịch vụ phía sau đối với các dữ liệu ít thay đổi.
"FileCacheOptions": {
"TtlSeconds": 30,
"Region": "ProductDataRegion"
}
Cấu hình Quality of Service (QoS)
Sử dụng Polly để thiết lập Circuit Breaker, giúp hệ thống ngắt kết nối tạm thời khi một dịch vụ gặp sự cố liên tục.
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 5,
"DurationOfBreak": 5000,
"TimeoutValue": 3000
}
6. Giới hạn băng thông (Rate Limiting)
Để bảo vệ hệ thống khỏi các cuộc tấn công Brute-force hoặc lạm dụng API, Ocelot cung cấp tính năng Rate Limiting dựa trên ClientId.
"RateLimitOptions": {
"ClientWhitelist": ["AdminApp"],
"EnableRateLimiting": true,
"Period": "1m",
"PeriodTimespan": 15,
"Limit": 20
}
Trong cấu hình trên: Trong vòng 1 phút, một Client chỉ được phép gọi tối đa 20 lần. Nếu vi phạm, họ phải đợi 15 giây trước khi thử lại.
7. Kinh nghiệm xử lý lỗi thực tế
- Vấn đề giao thức (Scheme): Khi đăng ký dịch vụ với Consul, tránh bao gồm tiền tố
http://trong địa chỉ IP/Host. Hãy để Ocelot điều khiển việc này thông qua thuộc tínhDownstreamScheme. - Phiên bản Ocelot: Từ phiên bản 16.0.0 trở đi, từ khóa
ReRoutesđã được thay thế hoàn toàn bằngRoutes. Việc sử dụng từ khóa cũ có thể dẫn đến lỗi không nhận diện cấu hình. - Hợp nhất tệp cấu hình: Đối với các dự án lớn, nên chia nhỏ tệp cấu hình (ví dụ:
ocelot.auth.json,ocelot.order.json) và sử dụng phương thứcAddOcelot()để tự động gộp chúng lại khi ứng dụng khởi chạy.