1. Saga là gì? Mô hình saga
1.1 Saga là gì ?
Saga là một mô hình quản lý giao dịch phân tán giúp đảm bảo tính nhất quán dữ liệu trong hệ thống phân tán mà không cần sử dụng một giao dịch phân tán theo kiểu truyền thống. Mô hình này thường được áp dụng trong kiến trúc microservices, nơi mà một giao dịch có thể bao gồm nhiều bước được thực hiện qua nhiều dịch vụ khác nhau.
Trong mô hình Saga, một giao dịch phức tạp được chia thành nhiều bước nhỏ hơn và mỗi bước này là một giao dịch độc lập.
Mỗi bước trong Saga có hai phần chính:
- Action: Là hành động chính của bước đó, ví dụ như tạo đơn hàng, cập nhật tài khoản, v.v.
- Compensation: Là hành động bù trừ được thực hiện nếu một bước nào đó trong Saga thất bại (tương tự như “rollback” trong giao dịch truyền thống).
1.2 Mô hình Saga
Có hai loại mô hình Saga chính:
- Choreography-based Saga: Mỗi dịch vụ tự quản lý logic của mình và phát ra sự kiện để kích hoạt các dịch vụ khác mà không cần một điều phối viên trung tâm. Mỗi dịch vụ tự quyết định phải làm gì tiếp theo dựa trên các sự kiện nhận được.
- Orchestration-based Saga: Có một điều phối viên trung tâm (Saga Orchestrator) quản lý và điều phối từng bước của giao dịch. Orchestrator này sẽ quyết định bước nào cần thực hiện tiếp theo và cũng sẽ gọi đến các hành động bù trừ khi cần thiết.
1.3 Ưu / nhược điểm của Saga
- Ưu điểm của Saga:
- Đảm bảo tính nhất quán cuối cùng cho các hệ thống phân tán mà không cần khóa dữ liệu như trong giao dịch truyền thống.
- Tính linh hoạt trong xử lý lỗi, do mỗi dịch vụ chịu trách nhiệm riêng cho từng bước.
- Tăng khả năng mở rộng của hệ thống.
- Nhược điểm của Saga:
- Phức tạp hơn trong việc quản lý và xử lý lỗi.
- Đòi hỏi các dịch vụ phải tự quản lý các hành động bù trừ, khiến code có thể trở nên phức tạp.
2. Áp dụng Saga pattern trong microservice
2.1 Saga pattern trong microservice
Trong kiến trúc microservices, Saga pattern là một cách để quản lý giao dịch phân tán bằng cách chia một giao dịch lớn thành nhiều bước nhỏ, mỗi bước sẽ được xử lý bởi một dịch vụ riêng biệt. Do mỗi microservice thường có database riêng và tự quản lý dữ liệu của mình, khi một giao dịch yêu cầu các thay đổi ở nhiều microservices khác nhau, cần một cơ chế để đảm bảo tính nhất quán giữa chúng. Đây là lúc Saga pattern phát huy tác dụng.
2.2 Cách hoạt động của Saga pattern trong microservices
Saga pattern chia giao dịch phân tán thành một chuỗi các sub-transactions hoặc steps. Mỗi step là một hành động độc lập của một microservice, và nếu một bước nào đó thất bại, một quá trình compensation (bù trừ) sẽ được thực hiện để “hoàn tác” các bước đã thành công trước đó, đảm bảo hệ thống đạt được trạng thái nhất quán.
a) Choreography-based Saga trong microservice
- Mỗi microservice tham gia trong giao dịch sẽ phát ra các sự kiện (event) khi hoàn thành bước của mình. Các sự kiện này sẽ kích hoạt các dịch vụ khác thực hiện bước tiếp theo.
- Không có một điều phối viên trung tâm, thay vào đó các dịch vụ giao tiếp với nhau thông qua các sự kiện.
- Mỗi dịch vụ sẽ “lắng nghe” sự kiện và quyết định xem liệu nó có cần thực hiện bước của mình hay không.
– Ví dụ:
- Event 1: Order Service phát một sự kiện “Order Created”.
- Event 2: Payment Service lắng nghe sự kiện này và khi nhận được, nó sẽ trừ tiền từ tài khoản của khách hàng và phát một sự kiện “Payment Completed”.
- Event 3: Inventory Service nhận sự kiện “Payment Completed” và trừ hàng tồn kho.
– Ưu điểm của Choreography-based Saga
- Đơn giản, không cần một bộ điều phối trung tâm.
- Các microservice độc lập hơn, tăng khả năng mở rộng.
– Nhược điểm của Choreography-based Saga
- Phức tạp trong việc xử lý lỗi và đảm bảo thứ tự các sự kiện.
- Khó khăn trong việc theo dõi toàn bộ tiến trình của Saga vì các sự kiện phân tán khắp nơi.
b) Orchestration-based Saga
- Một dịch vụ điều phối trung tâm gọi là Saga Orchestrator sẽ quản lý toàn bộ tiến trình của giao dịch phân tán.
- Orchestrator này gọi các dịch vụ để thực hiện từng bước và theo dõi quá trình thực hiện.
- Nếu một bước thất bại, Orchestrator sẽ chỉ định các hành động bù trừ cho từng bước đã hoàn thành.
– Ưu điểm Orchestration-based Saga
- Dễ dàng theo dõi tiến trình của giao dịch và quản lý lỗi.
- Saga Orchestrator kiểm soát toàn bộ luồng giao dịch, giúp dễ dàng xử lý logic phức tạp.
– Nhược điểm Orchestration-based Saga
- Có thể tạo ra điểm nghẽn nếu Orchestrator trở thành điểm thất bại duy nhất (single point of failure).
- Làm tăng độ phức tạp của hệ thống do phụ thuộc vào Orchestrator.
– Ví dụ:
- Event 1: Orchestrator gọi Order Service để tạo đơn hàng.
- Event 2: Sau khi nhận phản hồi thành công từ Order Service, Orchestrator gọi Payment Service để trừ tiền.
- Event 3: Nếu việc thanh toán thất bại, Orchestrator sẽ yêu cầu Order Service hoàn tác đơn hàng.
– Order Service
@RestController @RequestMapping("/order") public class OrderController { @PostMapping public ResponseEntity createOrder(@RequestBody OrderRequest orderRequest) { // Logic để tạo đơn hàng return ResponseEntity.ok("Order Created"); } @PostMapping("/compensate") public ResponseEntity cancelOrder(@RequestBody OrderRequest orderRequest) { // Logic để hủy đơn hàng trong trường hợp cần bù trừ return ResponseEntity.ok("Order Canceled"); } } @Getter @Setter public class OrderRequest { private String orderId; private Double amount; }
– Payment Service
@RestController @RequestMapping("/payment") public class PaymentController { @PostMapping public ResponseEntity processPayment(@RequestBody PaymentRequest paymentRequest) { // Logic xử lý thanh toán return ResponseEntity.ok("Payment Processed"); } @PostMapping("/compensate") public ResponseEntity refundPayment(@RequestBody PaymentRequest paymentRequest) { // Logic hoàn trả trong trường hợp cần bù trừ return ResponseEntity.ok("Payment Refunded"); } } @Getter @Setter public class PaymentRequest { private String orderId; private Double amount; }
– Inventory Service
@RestController @RequestMapping("/inventory") public class InventoryController { @PostMapping public ResponseEntity reserveInventory(@RequestBody InventoryRequest inventoryRequest) { // Logic để trừ hàng trong kho return ResponseEntity.ok("Inventory Reserved"); } @PostMapping("/compensate") public ResponseEntity releaseInventory(@RequestBody InventoryRequest inventoryRequest) { // Logic hoàn trả hàng trong trường hợp cần bù trừ return ResponseEntity.ok("Inventory Released"); } } @Getter @Setter public class InventoryRequest { private String orderId; private Integer quantity; }
– Orchestrator Service
Saga Orchestrator sẽ điều phối các bước và xử lý lỗi khi có bước thất bại.
@Service @RequiredArgsConstructor public class OrderSagaOrchestrator { private final RestTemplate restTemplate; public void startOrderSaga(OrderRequest orderRequest) { try { // Bước 1: Tạo đơn hàng restTemplate.postForEntity("http://localhost:8081/order", orderRequest, String.class); // Bước 2: Xử lý thanh toán restTemplate.postForEntity("http://localhost:8082/payment", orderRequest, String.class); // Bước 3: Trừ hàng tồn kho restTemplate.postForEntity("http://localhost:8083/inventory", orderRequest, String.class); // Saga thành công System.out.println("Order Saga completed successfully."); } catch (Exception e) { System.out.println("Saga failed. Compensating transactions..."); compensateOrderSaga(orderRequest); } } private void compensateOrderSaga(OrderRequest orderRequest) { // Gọi đến các endpoint để bù trừ các bước đã thành công restTemplate.postForEntity("http://localhost:8081/order/compensate", orderRequest, String.class); restTemplate.postForEntity("http://localhost:8082/payment/compensate", orderRequest, String.class); restTemplate.postForEntity("http://localhost:8083/inventory/compensate", orderRequest, String.class); } }
// Orchestrator sẽ có một endpoint để bắt đầu Saga. @RestController @RequestMapping("/order-saga") @RequiredArgsConstructor public class OrderSagaController { private final OrderSagaOrchestrator orchestrator; @PostMapping public ResponseEntity startOrderSaga(@RequestBody OrderRequest orderRequest) { orchestrator.startOrderSaga(orderRequest); return ResponseEntity.ok("Order Saga started"); } }
– Giải thích cách hoạt động
Khi nhận được yêu cầu tạo đơn hàng, OrderSagaOrchestrator sẽ thực hiện các bước tuần tự:
- Tạo đơn hàng ở Order Service.
- Xử lý thanh toán ở Payment Service.
- Cập nhật kho hàng ở Inventory Service.
➡ Nếu tất cả các bước thành công, Saga hoàn tất.
➡ Nếu một bước nào đó thất bại, Orchestrator sẽ kích hoạt các endpoint bù trừ để hoàn tác các bước đã thực hiện thành công trước đó.