Giới thiệu về Saga Pattern trong microservices
Mô hình Saga đưa ra một giải pháp có cấu trúc để giải quyết thách thức này. Nó cung cấp một phương pháp có hệ thống để quản lý transaction qua nhiều microservices. Điều này giải quyết những phức tạp của các transaction phân tán và hoàn toàn tương thích với các nguyên tắc của kiến trúc microservices, được đặc trưng bởi sự kết nối lỏng lẻo và khả năng triển khai độc lập của các service.
Saga Pattern trong ứng dụng microservices
Các ứng dụng monolithic truyền thống chủ yếu phụ thuộc vào các thuộc tính ACID
để duy trì tính toàn vẹn dữ liệu. Tuy nhiên, khi các ứng dụng trở nên phức tạp hơn, nhược điểm của mô hình monolithic trở nên rõ ràng hơn. Trong khi kiến trúc microservices hiệu quả trong việc giải quyết nhiều hạn chế này, nó cũng mang lại một thách thức lớn trong việc quản lý transaction và đảm bảo tính nhất quán dữ liệu trên nhiều hệ thống database và service độc lập.
Mô hình SAGA đưa ra một giải pháp có cấu trúc để giải quyết thách thức này. Nó cung cấp một phương pháp có hệ thống để quản lý transaction qua nhiều microservices. Điều này giải quyết những phức tạp của các transaction phân tán và hoàn toàn tương thích với các nguyên tắc của kiến trúc microservices, được đặc trưng bởi sự kết nối lỏng lẻo và khả năng triển khai độc lập của các service.
ACID là gì
ACID (viết tắt của Atomicity, Consistency, Isolation, Durability) là một khái niệm quan trọng trong lĩnh vực cơ sở dữ liệu và kiến trúc ứng dụng, nơi các chuyên gia thường tập trung đánh giá khi xem xét các hệ thống này. Để đảm bảo độ tin cậy của một cơ sở dữ liệu, cần phải đạt được cả bốn thuộc tính quan trọng này.
Atomicity đề xuất một transaction làm tất cả hoặc không làm gì cả. Điều này đảm bảo rằng khi một transaction liên quan đến hai hoặc nhiều xử lý, thì một là tất cả đều xử lý thành công - hoặc không có xử lý nào được thực hiện.
Consistency đảm bảo rằng một transaction không bao giờ chấp nhận trạng thái không đồng nhất trong cơ sở dữ liệu. Thuộc tính này , hoặc là tạo ra toàn bộ trạng thái mới hoặc revert tất cả các xử lý để quay về trạng thái ban đầu, đảm bảo không bao giờ xuất hiện trạng thái không ổn định.
Isolation duy trì sự tách rời giữa các transaction cho đến khi chúng hoàn tất. Điều này đảm bảo rằng hai hoặc nhiều transaction không bao giờ conflict, ngăn chặn việc tạo ra dữ liệu không chính xác hoặc không thống nhất.
Durability đảm bảo rằng cơ sở dữ liệu theo dõi các thay đổi một cách an toàn để máy chủ có thể khôi phục từ một kết thúc bất thường. Thuộc tính này đảm bảo rằng dữ liệu sẽ không bị mất trong trường hợp lỗi hoặc khởi động lại dịch vụ, giữ cho dữ liệu luôn sẵn sàng trước khi xảy ra sự cố.
Tại sao chúng ta cần mô hình Saga?
Kiến trúc Microservices cho phép chúng ta phát triển và duy trì các application service
cũng như database
của chúng một cách riêng biệt. Tuy nhiên, với sự phức tạp của yêu cầu hiện đại, việc thay đổi dữ liệu có thể cần phải lan truyền và cập nhật qua nhiều service
.
Mô hình Saga là một kiểu thiết kế kiến trúc trong lĩnh vực microservices, nơi các service nhỏ và độc lập tương tác với nhau thông qua các event
. Mô hình Saga giải quyết vấn đề của quản lý transaction phân tán và giúp đảm bảo tính nhất quán trong hệ thống.
Ví dụ: Hệ thống đặt hàng trực tuyến
Giả sử bạn đang xây dựng một hệ thống đặt hàng trực tuyến dựa trên kiến trúc microservices. Có hai dịch vụ chính là OrderService
và PaymentService
.
- Khởi tạo Đặt hàng:
- Khi người dùng tạo đơn hàng mới, dịch vụ
OrderService
bắt đầu quá trình xác nhận đơn hàng và kiểm tra tính khả dụng của sản phẩm. - Sau khi xác nhận, nó phát ra một sự kiện
ORDER_CONFIRMED
.
- Khi người dùng tạo đơn hàng mới, dịch vụ
- Thanh toán:
- Dịch vụ
PaymentService
nghe sự kiệnORDER_CONFIRMED
và bắt đầu quá trình thanh toán. - Sau khi thanh toán thành công, nó phát ra sự kiện "
PAYMENT_PROCESSED
".
- Dịch vụ
- Xác nhận hoàn tất:
- Dịch vụ
OrderService
lắng nghe eventPAYMENT_PROCESSED
và xác nhận đơn hàng đã hoàn tất. - Nếu có vấn đề nào đó, nó phát ra event
ORDER_CONFIRMED_ERROR
.
- Dịch vụ
Trong mô hình Saga, mỗi bước trong quá trình này có thể được thực hiện bởi một microservice riêng biệt. Nếu có lỗi xảy ra ở bất kỳ bước nào, mô hình Saga giúp đảm bảo rằng hệ thống không rơi vào trạng thái không nhất quán.
Chúng ta cần một cơ chế thông báo và điều phối tập trung để đảm bảo tất cả các transaction được thực hiện thành công, và đó là nơi mô hình Saga xuất hiện.
Mô hình Saga giúp hệ thống microservices trở nên linh hoạt hơn và dễ quản lý khi cần xử lý các sự cố và bảo đảm tính nhất quán trong quá trình giao tiếp giữa các service.
Nếu có lỗi xảy ra trong quá trình thanh toán, mô hình Saga có thể thực hiện các bước hoàn tác (compensation) để đảm bảo rằng đơn hàng không được xác nhận mà chưa được thanh toán thành công.
SAGA pattern là gì
Mô hình Saga là một mẫu thiết kế được sử dụng để quản lý transaction
và đảm bảo tính nhất quán dữ liệu qua nhiều service trong một hệ thống phân tán (distributed system), đặc biệt là trong kiến trúc microservices.
Biểu đồ dưới đây chứa các rollback events được đánh dấu đỏ, đó là một phần của quy trình làm việc của Orchestration-Based Saga:
Những event này được kích hoạt bởi Saga Execution Controller
trong trường hợp xảy ra sự cố để đảm bảo tính ổn định của hệ thống.
Saga Execution Controller
(SEC) là thành phần tập trung kiểm soát các local transactions và rollback events. Nó theo dõi tất cả các event của các transaction phân tán như một chuỗi thứ tự và quyết định về các rollback event trong trường hợp xảy ra sự cố. Bên cạnh đó, SEC đảm bảo rằng các rollback event không có bất kỳ side effects nào ngoài việc đảo ngược (reversing) các local transactions.
Note: SEC sử dụng một bản ghi nội bộ được gọi là Saga Log để theo dõi tất cả các transaction.
Khác với các ứng dụng monolithic truyền thống nơi một cơ sở dữ liệu đơn có thể xử lý tính nhất quán, microservices thường sử dụng nhiều cơ sở dữ liệu khác nhau, làm cho việc duy trì tính toàn vẹn dữ liệu trên toàn bộ hệ thống sử dụng các transaction ACID tiêu chuẩn trở nên vô cùng khó khăn. Mô hình Saga giải quyết vấn đề này bằng cách chia một transaction
thành các transaction
nhỏ hơn, được xử lý bởi các service khác nhau.
Có 3 thành phần chính trong mô hình Saga bạn cần biết để hiểu cách nó hoạt động:
- Local Transactions - Mỗi bước trong business process được thực hiện như là một
local transaction
trong service tương ứng của nó. - Compensation Transactions - Nếu một trong những
local transaction
thất bại,compensation transactions
được kích hoạt trong các service nơi các bước trước đó đã được thực hiện thành công. Nhữngcompensation transactions
này về cơ bản là các undo operations, đảm bảo hệ thống trở về trạng thái nhất quán ban đầu. - Communication - Các dịch vụ communicate với nhau thông qua các
messages
hoặcevents
. Điều này có thể là đồng bộ hoặc, phổ biến hơn, là bất đồng bộ sử dụng message queues hoặc event buses.Saga Execution Controller
kích hoạt nhữngevents
này trong trường hợp có sự cố để đảm bảo tính ổn định của hệ thống.
Cách triển khai mô hình SAGA
Có 2 phương pháp để triển khai mô hình Saga: Orchestration-Based Saga
(Command based) và Choreography-Based Saga
(Event based).
Chúng ta hãy cùng tham khảo từng phương pháp dựa trên một ví dụ thực tế sau đây:
Một hệ thống đơn giản Booking Service Online
được xây dựng với kiến trúc Microservices mà tại đó có các service riêng lẻ như sau:
- Đặt vé (
BookingService
) - Xử lý thanh toán (
PaymentService
) - Cập nhật số ghế còn trống (
SeatUpdating
) - Gửi xác nhận đến khách hàng (
NotificationService
)
Giả sử một khách hàng thực hiện việc đặt vé. Điều này đòi hỏi kích hoạt nhiều service theo thứ tự để hoàn thành quy trình bắt đầu từ việc Booking
, Payment
, Reserve Seat
và Send Confirmation
.
1. Triển khai bằng Orchestration-Based Saga
Trong mô hình Orchestration-Based Saga
, một orchestrator
(người điều phối) duy nhất sẽ quản lý tất cả các transaction và chỉ đạo các service thực hiện các local transactions.
Orchestrator
hoạt động như một bộ điều khiển tập trung của tất cả các local transactions này và duy trì status của toàn bộ transaction.
SEC sẽ theo dõi tất cả các event này bằng cách sử dụng SEC Saga Log và thực hiện các rollback events trong trường hợp xảy ra sự cố.
Lợi ích:
- Phù hợp cho các quy trình làm việc phức tạp liên quan đến nhiều service cùng tham gia.
- Thích hợp khi có kiểm soát đối với từng service tham gia trong quy trình và kiểm soát đối với luồng hoạt động.
- Không tạo ra cyclic dependencies, vì
orchestrator
phụ thuộc một chiều vào các service tham gia của Saga. - Các service tham gia không cần biết về các xử lý cũng như event của các service tham gia khác. Sự phân chia rõ ràng về mặt quan tâm giúp đơn giản hóa business logic.
Nhược điểm:
- Phức tạp hóa thiết kế, yêu cầu triển khai thêm một logic
orchestration
. - Có một
point of failure
bổ sung, vìorchestrator
quản lý toàn bộ quy trình làm việc. Nếuorchestrator
gặp sự cố thì toàn bộ quy trình đều ngừng hoạt động. - Bottle neck/Letancy: khi số lượng services trong qua trình xử lý tăng lên cao thì rất dễ bottle neck.
- Design hệ thống không cẩn thận cũng rất dễ dính vào cái bẫy distributed monolithic application.
Hãy xem xét ví dụ mình giải thích trước đó và chia nhỏ mô hình Orchestration-Based Saga
thành các bước:
Bước 1 — Người dùng gửi yêu cầu Booking
Khi người dùng gửi yêu cầu Booking
mới, BookingService
nhận yêu cầu POST
và tạo orchestrator
mới cho quá trình Booking
.
Bước 2 — Orchestrator tạo Booking mới
Sau đó, Orchestrator
tạo một Booking
mới trong trạng thái PENDING
và gửi command PAYMENT_PROCESS
đến PaymentService
.
Bước 3 — Thực hiện tất cả các dịch vụ tương ứng
Sau khi PaymentService
xử lý thành công, orchestrator
sẽ gọi service SeatUpdating
để thực hiện tiếp quy trình.
Tương tự, tất cả các service sẽ được gọi một cách tuần tự, và orchestrator
sẽ được thông báo về việc transaction SUCCESS
hay FAIL
.
Bước 4 — Chấp nhận hoặc từ chối Booking
Sau khi mỗi service hoàn tất local transactions của mình, chúng sẽ thông báo status
của transaction cho orchestrator
. Dựa trên phản hồi đó, orchestrator
có thể Approve
hoặc Reject
Booking bằng cách cập nhật state của Booking
.
Nhưng nếu transaction SeatUpdating
thất bại thì sao? Service SeatUpdating
sẽ thông báo cho SEC
về sự cố, và SEC
sẽ bắt đầu các rollback các events tương ứng và kết thúc quá trình Booking
bằng cách đặt state của Booking
thành FAIL
.
Mô hình Orchestration-Based Saga
phức tạp hơn so với mô hình Choreography-Based Saga
, tuy nhiên đổi lại, nó phù hợp trong các tình huống như:
- Khi đã triển khai sẵn các microservices.
- Khi một số lượng lớn các microservices tham gia vào một transaction duy nhất.
2. Triển khai bằng Choreography-Based Saga
Trong mô hình Choreography-Based Saga
, tất cả các service tham gia vào transaction phân tán sẽ emit một event mới sau khi hoàn thành các local transactions của chúng.
Phương pháp Choreography-Based Saga
không có orchestrator
để chỉ đạo và thực hiện các local transactions. Thay vào đó, mỗi service sẽ chịu trách nhiệm tạo ra một event mới và nó sẽ kích hoạt transaction của service tiếp theo.
Lợi ích của mô hình này
- Phù hợp cho các quy trình làm việc đơn giản yêu cầu ít bên tham gia và không cần logic điều phối.
- Không đòi hỏi triển khai và bảo trì dịch vụ bổ sung.
- Trách nhiệm được phân phối qua các bên tham gia của Saga, do đó không sinh ra
point of failure
.
Nhược điểm:
- Quy trình làm việc có thể trở nên rối hơn khi thêm các step mới, đồng thời khó theo dõi được service nào của Saga đang lắng nghe sự kiện nào nào.
- Có rủi ro về cyclic dependencies giữa các bên tham gia của Saga vì chúng phải consume các message của nhau.
- Quy trình Kiểm thử tích hợp khó khăn vì tất cả các service phải đang chạy để mô phỏng một transaction.
- Ngoài ra việc các business logic phân tán ở nhiều chỗ khiến việc hiểu toàn bộ flow trở nên khó khăn hơn.
Ví dụ, quá trình Booking
chúng ta đã thảo luận trước đó sẽ trông như sau với phương pháp Choreography-Based Saga
:
Như bạn có thể thấy, phương pháp này đơn giản hơn so với phương pháp Orchestration-Based Saga
. Vì vậy, phương pháp Choreography-Based Saga
thích hợp hơn trong các tình huống như:
- Khi triển khai các microservices mới từ đầu.
- Khi một số lượng nhỏ các microservices tham gia vào một
transaction
duy nhất.
Các vấn đề cần xem xét khi triển khai mô hình Saga:
- Mô hình Saga có thể khá khó khăn ban đầu, vì nó đòi hỏi cách tư duy mới về cách phối hợp một transaction và duy trì tính nhất quán dữ liệu cho một business process bao gồm nhiều microservices.
- Mô hình Saga khó debug, và độ phức tạp tăng lên khi số lượng service tham gia tăng lên.
- Dữ liệu khó có thể được rollback khi mà các service tham gia của Saga commit các thay đổi vào local databases của chúng.
- Khi triển khai phải có khả năng xử lý một tập hợp các lỗi tạm thời có thể xảy ra và cung cấp tính đồng nhất để giảm thiểu các
side effects
và đảm bảo tính nhất quán dữ liệu.
Đồng nhất có nghĩa là cùng một hoạt động có thể được lặp lại nhiều lần mà không làm thay đổi kết quả ban đầu. - Nên triển khai
observability
để kiểm tra và theo dõi quy trình làm việc của Saga. - Việc thiếu tính isolation của data của các service tham gia tạo ra thách thức về tính bền vững.
- Các hiện tượng bất thường sau có thể xảy ra nếu không có các biện pháp phòng ngừa thích hợp:
- Lost updates - Mất cập nhật, khi một Saga ghi mà không đọc những thay đổi được thực hiện bởi một Saga khác.
- Dirty reads - Đọc không an toàn, khi một giao dịch hoặc một Saga đọc các cập nhật được thực hiện bởi một Saga khác mà chưa hoàn thành những cập nhật đó.
- Fuzzy/nonrepeatable reads - Đọc mơ hồ/không lặp lại, khi các step khác nhau của Saga đọc dữ liệu khác nhau vì có một thay đổi dữ liệu bất thường xảy ra ở giữa.
- Các biện pháp phòng ngừa để giảm thiểu hoặc ngăn chặn các hiện tượng bất thường bao gồm:
- Semantic lock - Khóa ngữ nghĩa, một khóa cấp ứng dụng mà
transaction
có thể sử dụng để chỉ định một thay đổi đang diễn ra và chưa hoàn thành. - Commutative updates - Cập nhật giao hoán - có thể thực hiện theo bất kỳ thứ tự nào và tạo ra cùng một kết quả.
- Pessimistic view - Quan điểm bi quan: Có khả năng một Saga đọc dữ liệu không an toàn trong khi Saga khác đang chạy một
compensation
để rollback.
Quan điểm bi quan sắp xếp lại Saga để cập nhật dữ liệu cơ bản trong một transaction có thể thử lại, loại bỏ khả năng dirty reads. - Reread value - Đọc lại giá trị - xác nhận rằng dữ liệu không thay đổi, sau đó cập nhật lại log. Nếu log đã thay đổi, các bước sẽ bị hủy bỏ và Saga có thể khởi động lại.
- Tạo ra một tệp
versioning
ghi lại các thao tác trên một bản ghi khi chúng được yêu cầu, sau đó thực hiện theo đúng thứ tự. - Tuỳ theo giá trị yêu cầu mà lựa chọn các cơ chế xử lý concurrency phù hợp.
- Semantic lock - Khóa ngữ nghĩa, một khóa cấp ứng dụng mà