API Gateway là gì? Cài đặt Ocelot trong ASP.NET Core – Chặng đường trong Lộ trình .NET

Chào mừng các bạn đã trở lại với chuỗi bài viết “Lộ trình .NET”. Sau khi cùng nhau khám phá nhiều khía cạnh quan trọng từ kiến thức nền tảng C#, hệ sinh thái .NET, quản lý mã nguồn, HTTP/HTTPS, đến cơ sở dữ liệu, ORM như EF Core, Dapper, hay các kỹ thuật nâng cao như caching, dependency injection, logging và xây dựng API (RESTful, GraphQL, gRPC), chúng ta đã tích lũy được một lượng kiến thức đáng kể để xây dựng các ứng dụng mạnh mẽ. Hôm nay, chúng ta sẽ cùng tìm hiểu một khái niệm cực kỳ quan trọng trong kiến trúc ứng dụng hiện đại, đặc biệt là Microservices: API Gateway, và cách triển khai nó với Ocelot trong ASP.NET Core.

Nếu bạn mới bắt đầu hoặc muốn xem lại lộ trình của chúng ta, bạn có thể ghé thăm bài viết gốc: ASP.NET Core Roadmap – Lộ trình học ASP.NET Core 2025.

API Gateway là gì và Vì sao cần nó?

Trong kỷ nguyên của các ứng dụng phân tán, đặc biệt là kiến trúc Microservices, chúng ta thường có nhiều dịch vụ nhỏ, độc lập, mỗi dịch vụ chịu trách nhiệm cho một nghiệp vụ cụ thể (ví dụ: dịch vụ người dùng, dịch vụ sản phẩm, dịch vụ đơn hàng…). Các dịch vụ này có thể được phát triển, triển khai và mở rộng một cách độc lập.

Ban đầu, khi số lượng dịch vụ còn ít, việc các ứng dụng client (web, mobile) gọi trực tiếp đến từng dịch vụ riêng lẻ có vẻ đơn giản. Tuy nhiên, khi số lượng dịch vụ tăng lên, phương pháp này bộc lộ nhiều vấn đề:

  • Tăng độ phức tạp cho client: Client cần biết địa chỉ của tất cả các dịch vụ cần thiết và phải xử lý logic gọi nhiều dịch vụ để hoàn thành một tác vụ (ví dụ: trang chi tiết sản phẩm có thể cần gọi dịch vụ sản phẩm, dịch vụ đánh giá, dịch vụ khuyến mãi…).
  • Tăng số lượng round trips: Một yêu cầu từ client có thể dẫn đến nhiều lời gọi mạng đến các backend services, làm tăng độ trễ và ảnh hưởng hiệu năng.
  • Xử lý xuyên suốt (Cross-cutting concerns): Các vấn đề như xác thực (Authentication), ủy quyền (Authorization), giới hạn tần suất truy cập (Rate Limiting), ghi log (Logging) cần được triển khai ở mỗi dịch vụ, dẫn đến trùng lặp code và khó quản lý tập trung.
  • Quản lý phiên bản API: Việc thay đổi API của một dịch vụ có thể ảnh hưởng trực tiếp đến tất cả các client gọi đến nó.
  • Bảo mật: Việc cho client truy cập trực tiếp vào các internal services có thể tiềm ẩn rủi ro bảo mật.

Đây chính là lúc API Gateway phát huy vai trò của mình.

API Gateway là một điểm truy cập duy nhất (single entry point) cho tất cả các yêu cầu từ client đến hệ thống backend. Thay vì client gọi trực tiếp các microservices, họ sẽ gọi đến API Gateway. Gateway sau đó sẽ định tuyến (route) yêu cầu đó đến (các) dịch vụ backend phù hợp, có thể tổng hợp kết quả từ nhiều dịch vụ, và trả về cho client.

Hãy tưởng tượng API Gateway như một người “tiếp tân thông minh” hoặc “điều phối viên giao thông” ở cửa ngõ hệ thống của bạn. Tất cả các yêu cầu từ bên ngoài đều phải đi qua anh ta. Anh ta biết rõ ai được phép vào (xác thực), đi đâu (định tuyến), tốc độ đi như thế nào (giới hạn tần suất), và có thể gom các yêu cầu lại trước khi chuyển cho bộ phận chuyên trách.

Lợi ích của việc sử dụng API Gateway

Việc áp dụng API Gateway mang lại nhiều lợi ích thiết thực, giúp hệ thống của bạn trở nên mạnh mẽ, dễ quản lý và bảo mật hơn:

  • Đơn giản hóa Client: Client chỉ cần giao tiếp với một điểm cuối duy nhất (API Gateway) và không cần biết về cấu trúc phức tạp hay vị trí của các microservices nội bộ.
  • Tập trung hóa các vấn đề xuyên suốt: Các tác vụ như xác thực, ủy quyền, giới hạn tần suất truy cập, ghi log có thể được xử lý tại Gateway thay vì rải rác ở từng dịch vụ. Điều này giúp giảm trùng lặp code và dễ dàng thay đổi, quản lý. (Nhắc lại về Logging: Ghi Log có Cấu Trúc với Serilog, Cấu hình NLog).
  • Tăng hiệu năng và giảm độ trễ: Gateway có thể tổng hợp các yêu cầu từ nhiều dịch vụ backend thành một phản hồi duy nhất cho client, giảm số lượng round trips.
  • Tăng cường bảo mật: Chỉ API Gateway được expose ra bên ngoài, các microservices nội bộ được ẩn sau lớp Gateway, giảm bề mặt tấn công. (Nhắc lại về HTTP/S: HTTP và HTTPS: Giải Thích Từ A-Z).
  • Chuyển đổi giao thức: Gateway có thể giúp chuyển đổi giữa các giao thức khác nhau. Ví dụ, client gọi bằng HTTP/REST nhưng Gateway gọi đến backend service bằng gRPC. (Nhắc lại về các giao thức API: RESTful API, GraphQL, OData, gRPC).
  • Quản lý Service Discovery: Gateway có thể tích hợp với các hệ thống Service Discovery để tìm kiếm địa chỉ của các dịch vụ backend đang chạy.

Dưới đây là bảng tóm tắt sự khác biệt giữa việc client gọi trực tiếp đến microservices và thông qua API Gateway:

Đặc điểm Client gọi trực tiếp Microservices Client gọi qua API Gateway
Điểm truy cập Nhiều điểm cuối (từng dịch vụ) Một điểm cuối duy nhất (Gateway)
Độ phức tạp Client Cao, cần biết chi tiết các dịch vụ backend Thấp, chỉ cần biết địa chỉ Gateway
Số lượng Round Trips Nhiều, mỗi lần gọi một dịch vụ Ít hơn, Gateway có thể tổng hợp
Xử lý xuyên suốt (Auth, Rate Limit,…) Triển khai rải rác ở từng dịch vụ Tập trung tại Gateway
Bảo mật Các dịch vụ nội bộ bị expose Các dịch vụ nội bộ được ẩn sau Gateway
Quản lý phiên bản API Khó khăn, thay đổi 1 service ảnh hưởng nhiều client Dễ dàng hơn, Gateway có thể xử lý chuyển đổi phiên bản

Rõ ràng, trong các hệ thống Microservices phức tạp, API Gateway là một thành phần gần như không thể thiếu.

Giới thiệu về Ocelot

Ocelot là một API Gateway mã nguồn mở (open-source), nhẹ và nhanh, được xây dựng đặc biệt cho các ứng dụng sử dụng .NET Core (nay là .NET 5+). Nó được thiết kế để hoạt động tốt với các microservices, cung cấp các tính năng cần thiết như định tuyến, xác thực, ủy quyền, giới hạn tần suất, cân bằng tải (load balancing) cơ bản, tổng hợp (aggregation), v.v.

Tại sao chọn Ocelot cho ASP.NET Core?

  • Native .NET: Được viết bằng C# và chạy trên .NET, tích hợp mượt mà với hệ sinh thái ASP.NET Core.
  • Cấu hình: Sử dụng file cấu hình JSON hoặc có thể tùy chỉnh bằng code. File JSON rất dễ đọc và quản lý.
  • Nhẹ và hiệu quả: Được thiết kế để có footprint nhỏ và xử lý request hiệu quả.
  • Tính năng phong phú: Hỗ trợ nhiều tính năng quan trọng của API Gateway.
  • Cộng đồng: Là một dự án mã nguồn mở phổ biến, có cộng đồng hỗ trợ tốt.

Ocelot không phải là API Gateway duy nhất cho .NET (còn có các lựa chọn khác hoặc các giải pháp platform-specific như Azure API Management, AWS API Gateway), nhưng nó là một lựa chọn tuyệt vời, miễn phí và linh hoạt cho các dự án ASP.NET Core.

Cài đặt Ocelot

Để bắt đầu sử dụng Ocelot, bạn cần tạo một dự án ASP.NET Core mới (chọn template Empty hoặc Web API). Dự án này sẽ đóng vai trò là API Gateway của bạn.

Bạn cần thêm package Ocelot vào dự án bằng NuGet Package Manager hoặc sử dụng .NET CLI. Nếu bạn đã làm quen với .NET CLI qua các bài viết trước (Tìm Hiểu Hệ Sinh Thái .NET, Làm Chủ .NET CLI), bạn có thể chạy lệnh sau trong thư mục gốc của dự án:

dotnet add package Ocelot

Lệnh này sẽ thêm tham chiếu đến package Ocelot trong file .csproj của bạn.

Cấu hình Ocelot với ocelot.json

Ocelot sử dụng một file cấu hình (thường là ocelot.json) để định nghĩa các route và các thiết lập global. File này mô tả cách các yêu cầu từ client đến Gateway sẽ được định tuyến đến các dịch vụ backend nào.

Tạo một file mới tên là ocelot.json ở thư mục gốc của dự án Gateway. Nội dung cơ bản của file này sẽ chứa một mảng Routes và một mục GlobalConfiguration (tùy chọn).

{
  "Routes": [
    // Định nghĩa các Route ở đây
  ],
  "GlobalConfiguration": {
    // Các thiết lập áp dụng cho tất cả các Route (tùy chọn)
  }
}

Mỗi đối tượng trong mảng Routes định nghĩa một quy tắc định tuyến duy nhất. Các thuộc tính quan trọng nhất của một Route bao gồm:

  • DownstreamPathTemplate: Đường dẫn template của dịch vụ backend.
  • DownstreamScheme: Giao thức của dịch vụ backend (e.g., “http”, “https”).
  • DownstreamHostAndPorts: Địa chỉ host và port của dịch vụ backend. Đây là một mảng, cho phép cấu hình nhiều instance cho Load Balancing.
  • UpstreamPathTemplate: Đường dẫn template mà client sẽ gọi đến Gateway.
  • UpstreamHttpMethod: Phương thức HTTP mà client sử dụng (GET, POST, PUT, DELETE,…). Có thể là một mảng để cho phép nhiều phương thức.

Ví dụ về một Route đơn giản:

Giả sử bạn có một dịch vụ User Service đang chạy tại địa chỉ http://localhost:5001 và có endpoint /api/users. Bạn muốn client gọi đến Gateway tại http://localhost:5000/gateway/users để truy cập endpoint này.

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/users",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/gateway/users",
      "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ]
    }
  ],
  "GlobalConfiguration": {}
}

Trong ví dụ này:

  • Client gọi đến /gateway/users trên Gateway.
  • Gateway nhận yêu cầu và định tuyến nó đến /api/users trên dịch vụ chạy tại http://localhost:5001.
  • Tất cả các phương thức HTTP (GET, POST, PUT, DELETE) đều được cho phép.

Bạn có thể định nghĩa nhiều Route trong mảng Routes để chuyển tiếp các yêu cầu đến các dịch vụ backend khác nhau.

Tích hợp Ocelot vào Dự án ASP.NET Core

Sau khi thêm package và tạo file cấu hình, bạn cần tích hợp Ocelot vào pipeline xử lý request của ASP.NET Core. Điều này được thực hiện trong file Program.cs (hoặc Startup.cs nếu bạn sử dụng phiên bản cũ hơn).

Đầu tiên, bạn cần cấu hình ứng dụng để đọc file ocelot.json. Điều này thường được thực hiện trong phần cấu hình của Host Builder.

using Ocelot.DependencyInjection;
using Ocelot.Middleware;

var builder = WebApplication.CreateBuilder(args);

// Thêm cấu hình Ocelot từ file ocelot.json
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);

// Thêm dịch vụ Ocelot vào DI container
builder.Services.AddOcelot(builder.Configuration);

var app = builder.Build();

// ... Các cấu hình middleware khác (Routing, Authentication, Authorization, ...)

// Sử dụng middleware Ocelot
app.UseOcelot().Wait(); // .Wait() để đảm bảo cấu hình Ocelot được nạp xong

app.Run();

Giải thích:

  • builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);: Dòng này thêm file ocelot.json vào nguồn cấu hình của ứng dụng. optional: false nghĩa là file này là bắt buộc, ứng dụng sẽ lỗi nếu không tìm thấy. reloadOnChange: true cho phép Ocelot tự động nạp lại cấu hình khi file ocelot.json thay đổi (hữu ích trong môi trường phát triển).
  • builder.Services.AddOcelot(builder.Configuration);: Dòng này đăng ký các dịch vụ cần thiết của Ocelot vào hệ thống Dependency Injection của .NET Core. (Bạn có thể xem lại về DI tại Hiểu Rõ Vòng Đời Dịch Vụ).
  • app.UseOcelot().Wait();: Dòng này thêm middleware của Ocelot vào pipeline xử lý request. Middleware này sẽ chặn các request đến Gateway và xử lý chúng dựa trên cấu hình trong ocelot.json. .Wait() được sử dụng vì UseOcelot() là một async method, và chúng ta cần chờ nó hoàn thành trước khi ứng dụng bắt đầu lắng nghe request.

Đảm bảo rằng app.UseOcelot() được gọi sau các middleware xử lý CORS, Authentication, Authorization (nếu có cấu hình riêng trước Gateway) nhưng thường là trước các middleware xử lý routing hoặc endpoint khác của chính ứng dụng Gateway (nếu Gateway có các endpoint riêng).

Ví dụ Minh họa Đơn Giản

Để minh họa rõ hơn, hãy tạo hai microservices đơn giản và một Gateway sử dụng Ocelot.

Bước 1: Tạo 2 Microservices giả lập

Tạo hai dự án ASP.NET Core Web API mới, ví dụ: UserServiceProductService. Mỗi dự án chỉ cần một Controller đơn giản.

UserService/Controllers/UsersController.cs:

using Microsoft.AspNetCore.Mvc;

[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
    [HttpGet]
    public IActionResult GetUsers()
    {
        return Ok(new[] { new { Id = 1, Name = "Alice" }, new { Id = 2, Name = "Bob" } });
    }
}

ProductService/Controllers/ProductsController.cs:

using Microsoft.AspNetCore.Mvc;

[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetProducts()
    {
        return Ok(new[] { new { Id = 101, Name = "Laptop" }, new { Id = 102, Name = "Keyboard" } });
    }
}

Chạy hai dịch vụ này trên các cổng khác nhau, ví dụ User Service trên cổng 5001 và Product Service trên cổng 5002.

Bước 2: Tạo API Gateway với Ocelot

Tạo một dự án ASP.NET Core mới, ví dụ ApiGateway. Cài đặt Ocelot như hướng dẫn ở trên.

Tạo file ocelot.json trong dự án ApiGateway với nội dung sau:

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/users",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/api/v1/users",
      "UpstreamHttpMethod": [ "GET" ]
    },
    {
      "DownstreamPathTemplate": "/api/products",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5002
        }
      ],
      "UpstreamPathTemplate": "/api/v1/products",
      "UpstreamHttpMethod": [ "GET" ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5000" // Địa chỉ của Gateway
  }
}

Cập nhật file Program.cs của dự án ApiGateway để tích hợp Ocelot như đã hướng dẫn ở trên.

Chạy dự án ApiGateway (ví dụ trên cổng 5000).

Kiểm tra:

  • Chạy cả ba ứng dụng (UserService, ProductService, ApiGateway).
  • Mở trình duyệt hoặc sử dụng công cụ như Postman, gọi đến địa chỉ của Gateway:
    • Gọi http://localhost:5000/api/v1/users. Yêu cầu này sẽ được Gateway định tuyến đến http://localhost:5001/api/users.
    • Gọi http://localhost:5000/api/v1/products. Yêu cầu này sẽ được Gateway định tuyến đến http://localhost:5002/api/products.

Bạn sẽ thấy Gateway trả về kết quả từ đúng dịch vụ backend tương ứng. Với client, họ chỉ cần biết địa chỉ của Gateway và các đường dẫn /api/v1/users hay /api/v1/products mà không cần quan tâm các dịch vụ backend thực sự đang chạy ở đâu.

Các Tính Năng Nâng Cao của Ocelot

Ocelot không chỉ dừng lại ở định tuyến cơ bản. Nó hỗ trợ nhiều tính năng mạnh mẽ khác mà bạn có thể cấu hình trong file ocelot.json hoặc thông qua các setup nâng cao:

  • Authentication & Authorization: Tích hợp với các hệ thống xác thực như IdentityServer, JWT Bearer. Bạn có thể cấu hình yêu cầu xác thực hoặc quyền cụ thể cho từng route.
  • Rate Limiting: Giới hạn số lượng request mà client được phép gửi trong một khoảng thời gian nhất định để chống lại các cuộc tấn công DDoS hoặc lạm dụng API.
  • Service Discovery: Ocelot có thể tích hợp với các service discovery provider như Consul hoặc Eureka để tự động tìm kiếm địa chỉ của các dịch vụ backend thay vì cấu hình địa chỉ tĩnh.
  • Load Balancing: Nếu bạn có nhiều instance của cùng một dịch vụ, Ocelot có thể phân phối request đến các instance này theo các thuật toán khác nhau (Round Robin, Least Connection,…).
  • Caching: Cache phản hồi từ các dịch vụ backend để tăng tốc độ và giảm tải cho backend. (Nhắc lại về Caching: Các Chiến Lược Cache, Sử dụng Redis).
  • Request Aggregation: Cho phép gom nhiều yêu cầu đến các dịch vụ backend khác nhau thành một yêu cầu duy nhất từ client.
  • Header Transformation: Thêm, xóa hoặc thay đổi các header trong request hoặc response khi đi qua Gateway.

Việc cấu hình các tính năng này đòi hỏi thêm các thuộc tính trong file ocelot.json và có thể cần thêm các package NuGet khác tương ứng với tính năng (ví dụ: Ocelot.Provider.Consul cho Service Discovery với Consul).

Kết Luận

API Gateway là một thành phần kiến trúc quan trọng trong việc xây dựng và quản lý các ứng dụng phân tán, đặc biệt là Microservices. Nó giúp đơn giản hóa phía client, tập trung hóa các tác vụ xuyên suốt và tăng cường bảo mật.

Ocelot là một lựa chọn API Gateway tuyệt vời cho các dự án ASP.NET Core nhờ sự tích hợp chặt chẽ, cấu hình linh hoạt và bộ tính năng phong phú. Việc cài đặt và cấu hình cơ bản Ocelot khá đơn giản, giúp bạn nhanh chóng thiết lập một điểm truy cập duy nhất cho các dịch vụ backend của mình.

Hiểu và biết cách triển khai API Gateway là một bước tiến quan trọng trên con đường trở thành một lập trình viên .NET thành thạo, đặc biệt là khi bạn làm việc với các kiến trúc hiện đại. Hãy tiếp tục thực hành với Ocelot, khám phá các tính năng nâng cao của nó và áp dụng vào các dự án của bạn.

Chặng đường học hỏi về .NET vẫn còn nhiều điều thú vị phía trước. Hãy cùng đón chờ các bài viết tiếp theo trong series này nhé!

Chỉ mục