+1

[Microservices Series] Bài 4: Các Service "nói chuyện" với nhau thế nào? (REST, gRPC hay Message Broker)

Chào anh em! Ở bài trước, chúng ta đã chia hệ thống thành nhiều mảnh ghép và thậm chí dùng cả Go lẫn Laravel để tối ưu sức mạnh. Nhưng chia ra rồi thì phải đối mặt với một vấn đề nhức nhối: Làm sao để các mảnh ghép này giao tiếp với nhau?

Trong Monolith, các class gọi nhau qua memory, tốc độ tính bằng nanosecond. Nhưng với Microservices, Service A gọi Service B phải đi qua môi trường mạng (Network). Nếu bạn chọn sai phương thức giao tiếp, hệ thống sẽ chậm như rùa bò, hoặc tệ hơn là sập dây chuyền (Cascading Failure).

Dưới đây là 3 "vũ khí" phổ biến nhất và cách mình lựa chọn chúng trong thực tế.

1. REST API (HTTP/1.1) – "Quốc dân" nhưng cồng kềnh

Đây là cách ai cũng nghĩ đến đầu tiên. Service A gọi một cục JSON bắn sang Service B qua giao thức HTTP.

  • Điểm cộng: Quá phổ biến, dễ hiểu, dễ debug. Bạn có thể dùng Postman chọc thẳng vào Service B để test.

  • Điểm trừ: Chậm và cồng kềnh. Header của HTTP/1.1 khá lớn, lại phải parse dữ liệu JSON (Encode/Decode) liên tục, gây tốn CPU.

  • Thực chiến: Mình rất hạn chế dùng REST API để các service gọi nội bộ (Internal) với nhau. REST chỉ nên dùng ở lớp ngoài cùng, tức là giao tiếp giữa Client (Web/App) với hệ thống (thông qua API Gateway).

2. gRPC (HTTP/2) – "Tốc độ bàn thờ" cho Internal Service

Được Google đẻ ra để giải quyết bài toán giao tiếp nội bộ. Thay vì truyền JSON, gRPC dùng Protocol Buffers (Protobuf) để mã hóa dữ liệu thành dạng nhị phân (binary) rồi truyền qua HTTP/2.

  • Điểm cộng: Tốc độ bàn thờ! Payload siêu nhỏ, parse dữ liệu cực nhanh. Nó còn hỗ trợ stream dữ liệu 2 chiều.

  • Điểm trừ: Khó debug hơn vì dữ liệu là binary (không đọc bằng mắt thường được như JSON). Cần cấu hình file .proto chặt chẽ giữa các service.

  • Thực chiến: Nhớ lại bài trước với hệ thống Metro của mình. Khi Service Cổng soát vé (viết bằng Go) cần gọi sang Service Ví điện tử (để kiểm tra số dư), mình bắt buộc phải dùng gRPC. Tốc độ phải tính bằng microsecond để cổng mở ngay lập tức, nếu dùng REST API thì khách hàng sẽ phải đứng khựng lại chờ cổng mở, gây ùn tắc giờ cao điểm.

3. Message Broker (Asynchronous) – "Cứ để lại lời nhắn, tôi xử lý sau"

REST và gRPC đều là giao tiếp Đồng bộ (Synchronous). Tức là Service A gọi Service B và phải đứng chờ B trả lời thì mới đi tiếp. Nếu B sập, A cũng nghẽn.

Để giải quyết, chúng ta dùng Giao tiếp Bất đồng bộ (Asynchronous) thông qua các Message Broker như RabbitMQ, Kafka hay Redis Pub/Sub.

  • Cách hoạt động: Service A không gọi thẳng Service B. A chỉ ném một "Sự kiện" (Event) vào Message Broker rồi đi làm việc khác. B, C, D rảnh lúc nào thì chui vào Broker lấy Event ra xử lý lúc đó.

  • Điểm cộng: Chống sập dây chuyền cực tốt (Decoupling). Nếu Service B (Gửi Email) sập, Service A (Tạo Đơn Hàng) vẫn hoạt động bình thường, event gửi mail sẽ được lưu tạm trong Broker chờ B sống lại xử lý tiếp.

  • Thực chiến: Khi hành khách quẹt thẻ qua ga, Gate Service chỉ cần mở cổng và ném một event TapIn_Success vào Kafka. Lúc này, Service Trừ tiền (Billing), Service Thống kê (Analytics), và Service Gửi thông báo (Notification) sẽ tự động subscribe Kafka để lấy dữ liệu về xử lý. Thằng Gate không cần quan tâm mấy ông kia làm gì, nó chỉ lo tập trung mở cổng cho người tiếp theo.

Kinh nghiệm "xương máu" chốt lại

Đừng chọn 1 cái, hãy dùng kết hợp:

  1. Từ Frontend gọi vào Backend: Dùng REST API (hoặc GraphQL).

  2. Service nội bộ cần kết quả ngay lập tức (Read data, Validate): Dùng gRPC.

  3. Service nội bộ không cần kết quả ngay, thực hiện thay đổi trạng thái (Write, Update, Send Notification): Bắt buộc dùng Message Broker / Event-Driven.

Tạm kết

Hiểu và chọn đúng phương thức giao tiếp sẽ giúp hệ thống của bạn không bị "thắt cổ chai" khi scale lên hàng triệu request. Tuy nhiên, khi các service gọi nhau tứ tung qua mạng, làm sao chúng ta biết một request từ User đang đi qua những service nào? Lỡ có một bước bị lỗi thì tìm ở đâu trong mớ hỗn độn 20 cái services?

Ở bài tiếp theo (Bài 5), mình sẽ chia sẻ về Distributed Tracing & Logging – Đèn pha soi sáng "mê cung" Microservices, giúp anh em Dev không bị trầm cảm khi tìm bug.

Hệ thống của anh em đang giao tiếp nội bộ bằng công cụ gì? Đã bao giờ nếm mùi cay đắng khi Service A chờ Service B đến mức timeout toàn hệ thống chưa? Cùng chia sẻ ở phần comment nhé!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí