Cấu trúc dữ liệu và Giải thuật Bài 99: API Gateway và Service Mesh - Tách biệt logic hạ tầng ra khỏi Application Code.
Trong Bài 98, chúng ta đã viết một đoạn code Golang xử lý thuật toán Token Bucket. Nếu bạn có 1 Microservice, việc tự viết đoạn code này rất đơn giản. Tuy nhiên, khi hệ thống của Hasaki mở rộng lên 50 Microservices (Order, Payment, Inventory, User, Notification...), một cơn ác mộng kiến trúc sẽ xuất hiện: Sự trùng lặp Code Hạ tầng (Infrastructure Code Duplication).
Bất kỳ một Service nào khi giao tiếp qua mạng cũng cần phải xử lý 4 rủi ro cơ bản:
-
Rate Limiting: Chặn Request nếu vượt quá ngưỡng tải.
-
Timeouts: Chờ quá 3 giây không thấy phản hồi thì phải tự động ngắt kết nối, không được treo luồng (Goroutine).
-
Retries: Nếu mạng chập chờn gây lỗi
503 Service Unavailable, phải thử lại (Retry) khoảng 3 lần trước khi báo lỗi thực sự. -
Circuit Breaker (Cầu dao tự ngắt): Nếu Payment Service đang sập, việc cố gắng Retry hàng nghìn lần chỉ làm nó sập nặng hơn (Cascading Failure). Phải có một "cầu dao" tự động ngắt toàn bộ Request gửi tới Payment trong 30 giây để nó có thời gian hồi sức.
Nếu bạn copy-paste 4 đoạn logic này vào bộ source code Golang (Laravel, Node.js) của cả 50 Services, việc bảo trì sẽ trở thành thảm họa. Nếu muốn thay đổi số lần Retry từ 3 xuống 2, bạn phải sửa code và Deploy lại toàn bộ 50 hệ thống!
Để giải phóng các lập trình viên backend khỏi nỗi ám ảnh này, API Gateway và Service Mesh ra đời với triết lý: Code ứng dụng chỉ nên chứa Business Logic. Mọi vấn đề về mạng (Networking) hãy để Hạ tầng lo.
1. API Gateway: Người bảo vệ Cửa khẩu (North-South Traffic)
API Gateway (như Kong, KrakenD, hoặc AWS API Gateway) là một máy chủ đứng ở rìa hệ thống. Mọi Request từ thiết bị của khách hàng (Bắc) đi vào hệ thống Microservices bên trong (Nam) đều phải đi qua cửa khẩu này.
Tại cửa khẩu, API Gateway sẽ thực thi các tác vụ tổng quan:
-
SSL Termination: Giải mã HTTPS thành HTTP để các Service bên trong giao tiếp cho nhẹ.
-
Authentication / JWT Validation: Kiểm tra xem Token của người dùng có hợp lệ không. Nếu không, trả về
401 Unauthorizedngay tại cửa khẩu, không làm phiền các Service bên trong. -
Global Rate Limiting: Áp dụng thuật toán (như Sliding Window ở Bài 98) trên Redis để chống DDoS.
Tuy nhiên, API Gateway bị "mù" trước những cuộc giao tiếp nội bộ. Nếu Order Service gọi sang Payment Service (Đông-Tây Traffic) và bị lỗi mạng, API Gateway không thể can thiệp để Retry hay ngắt Circuit Breaker.
2. Service Mesh: Lưới giao thông Nội bộ (East-West Traffic) và Sidecar Pattern
Để kiểm soát giao thông nội bộ giữa 50 Microservices mà không đụng chạm một dòng code ứng dụng nào, các kiến trúc sư (tiêu biểu là Lyft với dự án Envoy, và Google với Istio) đã tạo ra một mẫu thiết kế mang tên Sidecar Pattern.
Chiếc Sidecar giống hệt cái ghế phụ gắn bên cạnh một chiếc mô-tô.
-
Application Container (Mô-tô): Là code Golang của bạn, chỉ chứa đúng logic:
Gửi HTTP POST đến http://payment-service/charge. -
Sidecar Proxy (Ghế phụ - Envoy): Là một tiến trình proxy cực nhẹ (viết bằng C++) được cài đặt chạy song song, "ôm" chặt lấy Application Container trong cùng một Pod (đối với Kubernetes).
Phép thuật của Lưới (Mesh): Khi Order Service muốn gọi Payment Service, nó không gọi thẳng.
-
Code Go gửi Request đến cổng
localhostcục bộ. -
Chiếc Sidecar Proxy của Order chặn Request này lại. Nó kiểm tra xem Payment có đang bị ngắt cầu dao (Circuit Breaker) không.
-
Nếu an toàn, Sidecar của Order mã hóa gói tin (mTLS) và gửi qua mạng.
-
Chiếc Sidecar Proxy của Payment nhận gói tin, giải mã, kiểm tra Rate Limit cục bộ, rồi mới chuyển gói tin vào cho Code Go của Payment xử lý.
-
Nếu lúc truyền bị lỗi, Sidecar của Order sẽ tự động Retry 3 lần mà Code Go của bạn hoàn toàn không hề hay biết!
Khi hàng ngàn chiếc Sidecar này kết nối với nhau, chúng tạo thành một "Lưới dịch vụ" (Service Mesh) bao phủ toàn bộ hệ thống.
4. Tách biệt Cấu hình (Configuration) ra khỏi Code
Với Service Mesh (ví dụ Istio + Kubernetes), thay vì viết code vòng lặp for để Retry, bạn chỉ cần ném một file cấu hình định dạng YAML vào hệ thống điều khiển trung tâm (Control Plane). Control Plane sẽ tự động đẩy file cấu hình này xuống toàn bộ các Sidecar Envoy trong nháy mắt.
Một file cấu hình Circuit Breaker và Retry thực tế sẽ trông như thế này (không có một dòng logic nào lọt vào source code ứng dụng):
YAML
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: payment-service-route
spec:
hosts:
- payment-service
http:
- route:
- destination:
host: payment-service
# LOGIC RETRY: Tự động thử lại 3 lần nếu gặp lỗi mạng (503) hoặc timeout
retries:
attempts: 3
perTryTimeout: 2s
retryOn: connect-failure,503
# LOGIC TIMEOUT: Nếu tổng cộng thời gian quá 10s thì ngắt luôn
timeout: 10s
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: payment-circuit-breaker
spec:
host: payment-service
trafficPolicy:
outlierDetection:
# LOGIC CIRCUIT BREAKER:
consecutive5xxErrors: 5 # Bị lỗi 5xx đúng 5 lần liên tiếp...
interval: 10s # (trong khoảng thời gian 10 giây)
baseEjectionTime: 30s # ...thì tự động Ngắt Cầu Dao (Chặn 100% traffic) trong 30 giây!
maxEjectionPercent: 100
5. Tổng kết Hành trình Kiến trúc
Việc dịch chuyển từ Monolith sang Microservices giống như việc biến một siêu thị lớn thành một thành phố gồm hàng trăm cửa hàng nhỏ.
-
Cấu trúc dữ liệu và Thuật toán (Bài 1 - Bài 90): Là kỹ năng tối ưu hóa để quản lý hàng hóa bên trong từng cửa hàng (Xử lý Data trên RAM và Ổ cứng).
-
Hệ thống Phân tán (Raft, Sharding, Saga - Bài 91 - Bài 96): Là chuỗi cung ứng, kho bãi và ngân hàng để kết nối dữ liệu giữa các cửa hàng sao cho không bị thất thoát tiền bạc.
-
API Gateway & Service Mesh (Bài 97 - Bài 99): Chính là Bộ Công an và Sở Giao thông, lo việc điều tiết kẹt xe, kiểm tra giấy phép, và dập tắt các thảm họa dây chuyền.
Khi mọi thứ trên mạng lưới đã được tự động hóa ẩn đi, một câu hỏi cuối cùng được đặt ra cho các kỹ sư vận hành: Làm sao để biết một Request bị kẹt lại ở đâu giữa một "rừng" gồm 50 Microservices và 500 cái Sidecar?
All rights reserved