Chào các bạn, nhìn "tít le" của bài viết thì chắc các bạn cũng biết bài viết này viết về gì rồi nhỉ, đó chính là API Composite Pattern, một pattern được sử dụng trong Microservices Architecture, nó khá phổ biến và đơn giản. Nếu sau khi đọc tên xong các bạn không nhận ra hoặc không biết thì có thể là do các bạn đã biết, gặp hoặc làm nó rồi mà các bạn chưa biết tên của nó thôi. Nếu vẫn thấy mơ hồ thì cùng đọc tiếp xem nó là cái gì nhé.
1. Bối cảnh
Như chúng ta đã biết, trong kiến trúc microservices thì mỗi service sẽ chịu trách nhiệm cho một chức năng hoặc một phần của hệ thống. Do đó khi client cần dữ liệu từ nhiều service khác nhau, client sẽ cần phải gửi request đến nhiều service khác nhau.
Để cho dễ hình dung, chúng ta sẽ lấy ví dụ một hệ thống e-commerce siêu đơn giản (vì thực tế không hệ thống nào đơn giản thế cả😂) như sau. Ví dụ chúng ta có một hệ thống microservices như sau:
Trong đó:
- Client: Giao diện người dùng/frontend.
- API Gateway: Nhận request từ client, kiêm luôn xác thực người dùng bằng token.
- Order Service: Lấy dữ liệu đơn hàng.
- Customer Service: Lấy dữ liệu khách hàng.
- Product Service: Lấy dữ liệu sản phẩm.
Từ đó ta luồng hoạt động khi cần lấy thông tin về đơn hàng như sau:
- Client gửi request lấy dữ liệu đơn hàng đến API Gateway.
- API Gateway thực hiện xác thực request và thực hiện forward request đến Order Service.
- Order Service xử lý request và trả dữ liệu đơn hàng về cho Client.
- Client gửi request lấy dữ liệu khách hàng đến API Gateway.
- API Gateway thực hiện xác thực request và thực hiện forward request đến Customer Service.
- Customer Service xử lý request và trả dữ liệu khách hàng về cho Client.
- Client gửi request lấy dữ liệu sản phẩm đến API Gateway.
- API Gateway thực hiện xác thực request và thực hiện forward request đến Product Service.
- Product Service xử lý request và trả dữ liệu sản phẩm về cho Client.
- Client thực hiện tổng hợp và xử lý dữ liệu và hiển thị thông tin đơn hàng cho người dùng.
Như các bạn có thể thấy, lúc này Server của chúng ta nhận về 3 request để có thể lấy được thông tin của 1 đơn hàng, nếu đồng thời hệ thống của chúng ta có rất nhiều người sử dụng cùng lúc, thì có phải là số lượng request mà server cần chịu tải là gấp 3 lần lên đúng không. Đó là còn chưa tính đến chuyện mỗi request chúng ta cần phải xác thực request và nhiều thứ khác trước khi đến được service đích, trong khi 3 request này là gửi liên tiếp (hoặc đồng thời tùy theo client) nên việc xác thực gần như không cần thiết. Hơn nữa đây cũng là ví dụ đơn giản chỉ có 3 service thôi, hãy thử nghĩ hệ thống của bạn cần lấy dữ liệu từ chục service thì ... thôi khỏi nghĩ làm gì cho mệt 🤣.
Đúc kết lại ta có các vấn đề như sau:
- Tăng số lượng request: Client sẽ cần gửi nhiều request đến Server để lấy dữ liệu, điều đó có thể dẫn đến quá tải mạng và tăng độ phức tạp trong việc quản lý.
- Độ trễ cao: Mỗi request cần một thời gian nhất định để xử lý, đặc biệt khi phải request đến nhiều service khác nhau. Điều này sẽ làm tăng thời gian phản hồi tổng thể.
- Phức tạp hóa client logic: Client cần phải xử lý logic phức tạp để quản lý và kết hợp dữ liệu từ nhiều nguồn khác nhau.
Để giải quyết vấn đề này chúng ta có API Composite Pattern.
API Composite Pattern giải quyết những vấn đề này bằng cách tạo ra một tầng trung gian (thường gọi là Composite API) thực hiện các tác vụ sau:
- Nhận yêu cầu từ client.
- Gửi các yêu cầu tới nhiều microservices.
- Tổng hợp kết quả và trả về cho client dưới dạng một response duy nhất.
Đọc đến đây chắc các bạn cũng mường tượng được cách hoạt động của Composite API như thế nào rồi đúng không, còn nếu chưa hay vẫn mơ hồ thì ... đọc tiếp nhé 😁.
2. Cách hoạt động
Một chút lý thuyết trước khi đi vào cách hoạt động nhỉ 😀
API Composite Pattern là một pattern phổ biến thường được dùng trong hệ thống microservices, mục đích của pattern này là nhằm tối ưu hóa việc giao tiếp giữa các service và giảm số lượng request từ client. Nó cho phép client gửi một request duy nhất mà data trả về vẫn có thể đầy đủ từ các service khác nhau, nhờ vào việc tổng hợp (composite) data từ các service trước khi trả kết quả cuối cùng về cho client. Pattern này giúp tối ưu hóa hiệu suất, đơn giản hóa ở phía client.
2.1. Nguyên lý hoạt động
Nguyên lý hoạt động bao gồm các bước như sau:
- Client gửi request: Client sẽ gửi một request duy nhất đến Composite API.
- Composite API gửi request đến các service liên quan: Composite gửi tuần tự (hoặc đồng thời) các request tới các service liên quan để lấy dữ liệu cần thiết.
- Tổng hợp kết quả: Sau khi nhận được dữ liệu từ các service, Composite API sẽ thực hiện tổng hợp lại thành một kết quả là một response duy nhất.
- Trả về response cho Client: Composite API trả về một response duy nhất bao gồm đầy đủ thông tin được tổng hợp lại cho Client.
Vì là một pattern nên API Composite Pattern có thể có nhiều cách triển khai khác nhau, nhưng tựu chung lại thì nguyên lý hoạt động cơ bản sẽ bao gồm các bước như trên.
2.2. Ví dụ cụ thể
Ở đây mình sẽ thực hiện triển khai theo cách là dùng API Gateway cho Composite API luôn, ngoài cách này thì còn các cách khác nữa mình sẽ đề cập tại phần sau.
Mình sẽ dùng luôn ví dụ hệ thống e-commerce siêu đơn giản ban đầu nhé.
Khi client cần hiển thị thông tin về đơn hàng, bao gồm chi tiết về khách hàng và sản phẩm, họ cần lấy dữ liệu từ cả Order Service, Customer Service, và Product Service.
- Client gửi request đến cho API Gateway để lấy thông tin.
- API Gateway thực hiện xác thực request.
- API Gateway thực hiện request đến Order Service.
- Order Service xử lý request và trả dữ liệu đơn hàng về cho API Gateway.
- API Gateway thực hiện request đến Customer Service.
- Customer Service xử lý request và trả dữ liệu khách hàng về cho API Gateway.
- API Gateway thực hiện request đến Product Service.
- Product Service xử lý request và trả dữ liệu khách hàng về cho API Gateway.
- API Gateway thực hiện tổng hợp kết quả và trả về response cho Client.
Khá là tường minh và dễ hiểu đúng không nào.
Sau khi đọc xong luồng hoạt động kia, có thể sẽ có bạn sẽ thắc mắc: "Đang ở frontend tôi dùng được Async/Await/Promise luồng không bị block mà giờ đưa ra cái Pattern củ chuối này block hết luồng của tôi, chạy đồng bộ thì đến đời nào mới xong." Cũng đúng, nhưng thực tế hệ thống backend bây giờ không nhất thiết phải sử dụng tuần tự như thế, chúng ta hoàn toàn có thể sử dụng multithread (đa luồng) để gửi các request cùng lúc đến các service, hoặc sử dụng reactive programing (như trong Java thì có Spring Webflux) để xử lý vấn đề này.
3. Ưu và nhược điểm
Đương nhiên là khi muốn áp dụng bất cứ công nghệ hay pattern nào chúng ta cũng cần nghiên cứu về ưu và nhược điểm của nó. Sau đây chúng ta sẽ thống kê lại ưu và nhược điểm của API Composite Pattern, từ đó tự đưa ra kết luận xem có áp dụng được vào hệ thống của mình hay không nhé.
3.1. Ưu điểm
- Giảm số lượng request: Thay vì client phải gửi nhiều yêu cầu đến các service khác nhau, API Composite Pattern gộp các request thành một. Điều này giúp giảm tải cho mạng và đơn giản hóa logic xử lý tại client.
- Tối ưu hóa hiệu xuất: Bằng cách thực hiện các request song song tới các service nội bộ, Composite API có thể giảm tổng thời gian chờ (latency).
- Tăng tính đơn giản cho Client: Client không cần biết chi tiết cách thức hoạt động nội bộ của các service và cách dữ liệu được lấy từ chúng. Mọi logic phức tạp liên quan đến việc tổng hợp và xử lý dữ liệu đều được thực hiện bởi Composite API.
- Tính linh hoạt cao: Composite API có thể định dạng lại dữ liệu hoặc kết hợp các kết quả từ nhiều dịch vụ theo cách phù hợp với nhu cầu của client. Điều này giúp tách biệt sự thay đổi của backend với client, giúp hệ thống dễ bảo trì hơn.
- Tối ưu hóa việc xử lý lỗi: Composite API có thể xử lý các lỗi từ các service và cung cấp kết quả một cách thống nhất. Ví dụ nếu một microservice không hoạt động, Composite API có thể trả về dữ liệu mặc định hoặc thông báo lỗi phù hợp.
3.2. Nhược điểm
- Tăng độ phức tạp: Việc triển khai Composite API có thể phức tạp, đặc biệt khi số lượng service tăng lên hoặc các request có sự phụ thuộc lẫn nhau.
- Độ trễ trong một số trường hợp: Nếu một trong các service phản hồi chậm hoặc không khả dụng, điều này có thể ảnh hưởng đến toàn bộ thời gian phản hồi của request. Vì vậy, cần có cơ chế tối ưu hóa xử lý các dịch vụ chậm hoặc không khả dụng (như timeout hoặc fallback).
- Khó khăn trong việc bảo trì và mở rộng: Nếu hệ thống microservices mở rộng hoặc thay đổi, Composite API cần phải được cập nhật các thay đổi đó, dẫn đến khối lượng công việc bảo trì tăng lên.
- Không đảm bảo tính nhất quán ngay lập tức: Nếu các service sử dụng các cơ chế nhất quán cuối cùng (eventual consistency), dữ liệu trả về từ Composite API có thể không được đồng bộ ngay lập tức, đặc biệt khi có sự chậm trễ trong quá trình đồng bộ hóa dữ liệu.
3.3. Vậy khi nào nên sử dụng
- Khi Client cần dữ liệu từ nhiều nguồn khác nhau: Đặc biệt hữu ích khi client yêu cầu dữ liệu từ nhiều service trong cùng một thao tác, như trong các hệ thống thương mại điện tử, quản lý khách hàng, hoặc trang tổng quan (dashboard).
- Khi cần giảm số lượng request từ Client: Trong trường hợp hệ thống backend có cấu trúc phân tán với nhiều dịch vụ, việc gộp nhiều request lại thành một giúp giảm số lượng yêu cầu và tối ưu hóa hiệu suất mạng.
- Khi cần tối ưu hóa latency: Khi client cần dữ liệu tổng hợp từ nhiều dịch vụ, API Composite Pattern giúp giảm thời gian phản hồi tổng thể bằng cách gửi các request song song tới các service nội bộ.
4. Các cách triển khai phổ biến
Ngoài cách triển khai API Composite Pattern thực tiếp vào API Gateway như mình đã nói ở trên thì còn cách triển khai phổ biến khác như là: Tách riêng một service cho API Composite.
Và còn một số cách làm khác như là:
- Sử dụng GraphQL.
- Sử dụng Message Broker.
Vì bài đã dài nên mình sẽ không viết chi tiết các cách khác ở đây, các bạn có thể tham khảo bảng so sánh tổng quát bên dưới để tìm hiều và lựa chọn nhé:
Cách triển khai | Ưu điểm | Nhược điểm | Khi nào nên sử dụng |
---|---|---|---|
Composite API tại API Gateway | Tối ưu cho hệ thống đơn giản. | Tăng tải cho API Gateway, không phù hợp với luồng xử lý phức tạp. | Hệ thống nhỏ, yêu cầu đơn giản, không cần xử lý nhiều logic tổng hợp phức tạp. |
Composite API service riêng | Tối ưu cho logic phức tạp, dễ bảo trì và mở rộng. | Tăng số lượng service cần quản lý, độ phức tạp trong đồng bộ dữ liệu. | Hệ thống lớn, yêu cầu nhiều logic tùy chỉnh trong việc tổng hợp dữ liệu từ nhiều service. |
GraphQL | Linh hoạt cho client, giảm số lượng request. | Phức tạp trong triển khai, cần bảo mật chặt chẽ. | Khi cần sự linh hoạt trong việc truy vấn dữ liệu từ nhiều nguồn khác nhau và tối ưu hóa performance cho Client. |
Message Broker | Tính phi đồng bộ, dễ mở rộng, giảm tải hệ thống. | Độ trễ có thể cao hơn, không phù hợp với các yêu cầu đồng bộ. | Khi cần xử lý không đồng bộ, hệ thống có yêu cầu cao về khả năng mở rộng và chịu tải lớn. |
Reference
Kiến thức trong bài viết là kiến thức mình học và tổng hợp được từ trên mạng, nếu có phần nào chưa đúng hay chưa hợp lý các bạn hãy comment bên dưới để mình sửa lại nhé. Bài viết đến đây là kết thúc, cảm ơn các bạn đã đọc bài.
Bài viết thuộc series: Các Pattern phổ biến trong Microservices Architecture