Microservices

Microservices: Từ Thiết Kế Đến Triển Khai – Phần 5: Event-Driven Data Management

Đây là bài viết thứ năm trong loạt bài về Microservices: Từ Thiết Kế Đến Triển Khai. Trong bài đầu tiên, mô hình kiến trúc Microservices đã được giới thiệu và thảo luận về những ưu điểm và hạn chế trong việc ứng dụng và triển khai. Bài thứ hai mô tả về cách thức mà các client tương tác với các microservice thông qua một phương tiện trung gian gọi là API Gateway. Bài thứ ba tìm hiểu về cách mà các microservices trong cùng một hệ thống giao tiếp với nhau thông qua IPC (Inter-process Communication). Bài thứ tư chúng ta đã tìm hiểu qua về truy tìm service (Service Discovery). Trong bài viết này, hãy cùng xem xét các vấn đề về quản lý dữ liệu phân tán trong kiến ​​trúc microservices.

Để hiểu hơn về Event driven development, các bạn có thể đọc lại bài viết về Event-Driven Architecture trong series bài viết về Tổng quan về Software Architecture

(Các bạn có thể đọc bài viết gốc tiếng Anh tại đây: https://www.nginx.com/blog/event-driven-data-management-microservices/)

Microservices và vấn đề quản lý dữ liệu phân tán

Một ứng dụng nguyên khối thường có một cơ sở dữ liệu quan hệ duy nhất. Lợi ích chính của việc sử dụng cơ sở dữ liệu quan hệ là tính ACID.

ACID là từ viết tắt các chữ cái đầu của bốn từ tiếng Anh atomicity, consistency, isolation, và durability. Chúng được coi là bốn thuộc tính quan trọng của một hệ quản trị cơ sở dữ liệu khi xử lý bất kỳ transaction nào. Nếu thiếu một trong những thuộc tính này thì tính toàn vẹn của cơ sở dữ liệu khó có thể được đảm bảo. Trong một hệ quản trị cơ sở dư liệu, một transaction là một đơn vị lô gích thao tác trên dữ liệu, có thể bao gồm nhiều thao tác. Chẳng hạn việc chuyển tiền từ tài khoản này sang tài khoản khác là một transaction, bao gồm thao tác trừ tiền một tài khoản và cộng tiền vào tài khoản kia. Các tính chất ACID trong trường hợp này sẽ đảm bảo các transaction được thực hiện một cách đáng tin cậy:

  • Tính nguyên tố (Atomicity). Một transaction có nhiều thao tác khác biệt thì hoặc là toàn bộ các thao tác hoặc là không một thao tác nào được hoàn thành. Chẳng hạn việc chuyển tiền có thể thành công hay trục trặc vì nhiều lý do nhưng tính nguyên tố bảo đảm rằng một tài khoản sẽ không bị trừ tiền nếu như tài khoản kia chưa được cộng số tiền tương ứng.
  • Tính nhất quán (Consistency). Một transaction hoặc là sẽ tạo ra một trạng thái mới và hợp lệ cho dữ liệu, hoặc trong trường hợp có lỗi sẽ chuyển toàn bộ dữ liệu về trạng thái trước khi thực thi transaction.
  • Tính độc lập (Isolation). Một transaction đang thực thi và chưa được xác nhận phải bảo đảm tách biệt khỏi các transaction khác.
  • Tính bền vững (Durability). Dữ liệu được xác nhận sẽ được hệ thống lưu lại sao cho ngay cả trong trường hợp hỏng hóc hoặc có lỗi hệ thống, dữ liệu vẫn đảm bảo trong trạng thái chuẩn xác.

(Nguồn: https://vi.wikipedia.org/wiki/ACID)

Một lợi ích lớn khác của việc sử dụng một cơ sở dữ liệu quan hệ là có SQL, một ngôn ngữ truy vấn phong phú, khai báo và được chuẩn hóa. Bạn có thể dễ dàng viết truy vấn kết hợp dữ liệu từ nhiều bảng và có thể tối ưu. Và bởi vì tất cả dữ liệu của ứng dụng nằm trong một cơ sở dữ liệu nên rất dễ truy vấn.

Tuy nhiên, truy cập dữ liệu trở nên phức tạp hơn nhiều khi chúng ta chuyển sang microservices. Đó là bởi vì dữ liệu thuộc sở hữu của mỗi microservice là riêng tư đối với service đó và chỉ có thể được truy cập thông qua API của nó. Việc đóng gói dữ liệu đảm bảo rằng các microservice được kết hợp lỏng lẻo (loosely coupled) và có thể phát triển độc lập với nhau. Nếu nhiều service truy cập cùng một dữ liệu thì sẽ cần sự phối hợp của tất cả các service để đảm bảo tính nhất quán và toàn vẹn dữ liệu.

Thậm chí các microservice khác nhau thường sử dụng các loại cơ sở dữ liệu khác nhau. Các ứng dụng hiện đại lưu trữ và xử lý các loại dữ liệu đa dạng chứ không nhất thiết phải sử dụng cơ sở dữ liệu quan hệ. Trong một số trường hợp, sử dụng NoSQL sẽ thuận tiện và giúp tăng hiệu suất và khả năng mở rộng tốt hơn nhiều. Hoặc có thể sử dụng Elasticsearch cho lưu trữ và truy vấn văn bản. Do đó, các ứng dụng dựa trên microservices thường sử dụng hỗn hợp các cơ sở dữ liệu SQL và NoSQL được gọi là phương pháp “polyglot persistence“.

Một số thách thức trong quản lý dữ liệu phân tán.

Thách thức đầu tiên là làm thế nào để thực hiện các business transactions duy trì tính thống nhất trên nhiều service. Để xem tại sao đây là một vấn đề, chúng ta hãy xem một ví dụ về một cửa hàng B2B trực tuyến. Customer Service lưu trữ thông tin về khách hàng, bao gồm cả hạn mức tín dụng của họ. Order Service quản lý đơn đặt hàng và phải xác minh rằng đơn hàng mới không vượt quá giới hạn tín dụng của khách hàng. Trong phiên bản nguyên khối của ứng dụng này, Order Service chỉ cần sử dụng ACID transaction để kiểm tra tín dụng có sẵn và tạo đơn đặt hàng.

Ngược lại, trong kiến ​​trúc microservices, các bảng ORDER và CUSTOMER nằm ở các service khác nhau như diagram dưới đây.

1

Order Service không thể truy cập trực tiếp vào bảng CUSTOMER. Nó chỉ có thể sử dụng API được cung cấp bởi Customer Service. Order Service có thể sử dụng distributed transaction hay còn được gọi là two-phase commit (2PC). Tuy nhiên, 2PC thường không phải là một lựa chọn khả thi trong các ứng dụng hiện đại. Định lý CAP đòi hỏi bạn phải lựa chọn giữa tính sẵn sàng và tính nhất quán ACID, và tính sẵn sàng thường là sự lựa chọn tốt hơn. Hơn nữa, nhiều công nghệ hiện đại, chẳng hạn như hầu hết các cơ sở dữ liệu NoSQL, không hỗ trợ 2PC. Dù vậy, việc duy trì tính nhất quán dữ liệu giữa các service và cơ sở dữ liệu là rất cần thiết, cho nên chúng ta phải có giải pháp cho vấn đề này.

Thách thức thứ hai là cách triển khai các truy vấn lấy dữ liệu từ nhiều service. Ví dụ, hãy tưởng tượng rằng ứng dụng cần hiển thị một khách hàng và các đơn đặt hàng gần đây của anh ta. Nếu Order Service cung cấp một API để truy xuất các đơn đặt hàng của khách hàng thì bạn có thể truy xuất dữ liệu này bằng cách sử dụng các join dữ liệu từ nhiều service khác nhau bao gồm thông tin khách hàng từ Customer Service và các đơn đặt hàng của khách hàng từ Order Service. Tuy nhiên, giả sử rằng Order Service chỉ hỗ trợ tra cứu các đơn hàng bằng khóa chính của chúng (có lẽ NoSQL chỉ hỗ trợ các truy vấn dựa trên khóa chính). Trong tình huống này, không có cách rõ ràng để lấy dữ liệu cần thiết.

Event-Driven Architecture

Đối với nhiều ứng dụng, giải pháp là sử dụng Event-Driven Architecture. Trong kiến ​​trúc này, một service publish một event khi có gì đó đáng chú ý xảy ra, chẳng hạn như khi cập nhật một business entity. Các microservice khác đăng ký các event đó. Khi microservice nhận được một event, nó có thể cập nhật các business entity của riêng nó.

Bạn có thể sử dụng các event để update business transactions trên nhiều service. Một transaction bao gồm một loạt các bước. Mỗi bước bao gồm một microservice cập nhật một business entity và publish một event kích hoạt bước tiếp theo. Trình tự sơ đồ sau đây cho thấy cách sử dụng phương pháp tiếp cận theo event để kiểm tra tín dụng khi tạo đơn đặt hàng. Các event trao đổi microservices thông qua một Message Broker.

1. Order Service tạo Đơn đặt hàng có trạng thái NEW và publish event Order Created.

2

2. Customer Service lắng nghe và xử lý (consume) event Tạo đơn đặt hàng bằng cách dự trữ tín dụng cho đơn đặt hàng này và publish event Credit Reserved.

3

3. Order Service lắng nghe và xử lý event Credit Reserved và thay đổi trạng thái của đơn đặt hàng thành OPEN.

4

Với điều kiện (a) mỗi service cập nhật cơ sở dữ liệu một cách nguyên tố (atomically) và publish một event và (b) Message Broker đảm bảo rằng các event được gửi ít nhất một lần, bằng cách này bạn có thể thực hiện các business transactions trên nhiều service. Điều quan trọng cần lưu ý rằng nó không đảm bảo tính ACID mà chỉ có thể đạt được tính nhất quán cuối cùng (eventual consistency). Mô hình transaction này được gọi là BASE model.

Bạn cũng có thể sử dụng các event để duy trì chế độ xem được thực hiện trước (materialized views) khi kết hợp dữ liệu từ nhiều microservice. Service duy trì View sẽ xem đăng ký các event có liên quan và cập nhật lại View khi có thay đổi. Ví dụ: Customer Order View Updater Service duy trì chế độ xem Đơn hàng của khách hàng sẽ đăng ký các event được publish từ Customer Service và Order Service.

5

Event-driven architecture có một số lợi ích cũng như hạn chế. Nó cho phép thực hiện các transaction trên nhiều service và cung cấp tính nhất quán cuối cùng (eventual consistency). Một lợi ích khác là nó cũng cho phép một ứng dụng duy trì materialized views.

Một nhược điểm là mô hình lập trình phức tạp hơn so với việc sử dụng các transaction ACID. Thông thường, bạn phải thực hiện các transaction bù trừ để phục hồi từ các lỗi cấp ứng dụng; ví dụ: bạn phải hủy đơn hàng nếu kiểm tra tín dụng không thành công. Ngoài ra, các ứng dụng phải đối phó với dữ liệu không phù hợp khi bị thay đổi giữa chừng. Một hạn chế khác là subscribers phải phát hiện và bỏ qua các event trùng lặp.

Bảo đảm tính nguyên tố

Trong event-driven architecture, chúng ta thường gặp phải vấn đề về cập nhật cơ sở dữ liệu và xuất bản một event. Ví dụ, Order Service phải insert một row vào table ORDER và xuất bản (publish) một event (event) Order Created. Điều quan trọng là hai hoạt động này được thực hiện một cách nguyên tố. Nếu dịch vụ bị crash sau khi cập nhật cơ sở dữ liệu nhưng trước khi xuất bản event, hệ thống sẽ trở nên không nhất quán. Cách tiêu chuẩn để đảm bảo nguyên tố là sử dụng một distributed transaction liên quan đến cơ sở dữ liệu và Message Broker. Tuy nhiên, vì những lý do được mô tả ở trên, chẳng hạn như định lý CAP, đây chính xác là những gì chúng ta không muốn làm.

Publishing Events sử dụng Local Transactions

Một cách để đạt được nguyên tố là bằng cách sử dụng quy trình nhiều bước chỉ liên quan đến các Local Transactions. Bí quyết là có một bảng EVENT, có chức năng như một hàng đợi thông báo, trong cơ sở dữ liệu lưu trữ trạng thái của các thực thể nghiệp vụ. Ứng dụng bắt đầu một local transaction, cập nhật trạng thái của các thực thể nghiệp vụ, insert một event vào bảng EVENT và commits transaction đó. Một process khác biệt truy vấn bảng EVENT, và pulish các event cho Message Broker, và sau đó sử dụng một local transaction để đánh dấu các event được xuất bản như diagram dưới đây:

6

Order Service insert một hàng vào bảng ORDER và insert một Order Created event vào bảng EVENT. Event Publisher thread truy vấn bảng EVENT cho các event chưa được xuất bản để xuất bản các event và sau đó cập nhật bảng EVENT để đánh dấu các event được xuất bản.

Cách tiếp cận này có một số lợi ích và hạn chế. Một lợi ích là nó đảm bảo một event được xuất bản cho mỗi lần cập nhật mà không dựa vào 2PC. Ngoài ra, ứng dụng xuất bản các event cấp doanh nghiệp, giúp loại bỏ sự cần thiết phải suy ra chúng. Một nhược điểm của phương pháp này là nó có khả năng bị lỗi vì developer phải nhớ xuất bản các event. Một hạn chế của phương pháp này là nó rất khó thực hiện khi sử dụng một số cơ sở dữ liệu NoSQL vì khả năng truy vấn và transaction hạn chế của chúng.

Bây giờ chúng ta hãy xem xét một phương pháp đạt được tính nguyên tố khác.

Mining a Database Transaction Log

Một cách khác để đạt được nguyên tố mà không có 2PC là cho các event được xuất bản bởi một thread hoặc một process khai thác database’s transaction hoặc commit log. Ứng dụng cập nhật cơ sở dữ liệu, dẫn đến những thay đổi được ghi lại trong nhật ký transaction của cơ sở dữ liệu. Transaction Log Miner thread đọc nhật ký transaction và xuất bản event cho Message Broker như diagram dưới đây:

7

Một ví dụ về cách tiếp cận này là dự án mã nguồn mở LinkedIn Databus. Databus khai thác nhật ký transaction Oracle và xuất bản các event tương ứng với các thay đổi. LinkedIn sử dụng Databus để giữ các kho dữ liệu có nguồn gốc khác nhau phù hợp với hệ thống bản ghi.

Transaction log mining có nhiều lợi ích và hạn chế khác nhau. Một lợi ích là nó đảm bảo rằng một event được xuất bản cho mỗi lần cập nhật mà không cần sử dụng 2PC. Transaction log mining cũng có thể đơn giản hóa ứng dụng bằng cách tách xuất bản event khỏi logic nghiệp vụ của ứng dụng. Một nhược điểm lớn là định dạng của transaction log là khác nhau đối với mỗi cơ sở dữ liệu và thậm chí có thể thay đổi giữa các phiên bản cơ sở dữ liệu.

Sử dụng Event Sourcing

Event Sourcing đạt được tính nguyên tố mà không cần 2PC bằng cách sử dụng phương pháp tiếp cận event khác. Thay vì lưu trữ trạng thái hiện tại của một thực thể, ứng dụng lưu trữ một chuỗi các event thay đổi trạng thái. Ứng dụng xây dựng lại trạng thái hiện tại của một thực thể bằng cách phát lại các event. Bất cứ khi nào trạng thái của một thực thể nghiệp vụ thay đổi, một event mới được nối vào danh sách các event. Vì việc lưu một event là một hoạt động đơn lẻ, nó vốn dĩ là nguyên tố.

Để xem cách hoạt động của nguồn event, hãy xem xét thực thể Đơn hàng làm ví dụ. Trong cách tiếp cận truyền thống, mỗi đơn hàng ánh xạ tới một hàng trong bảng ORDER và tới các hàng trong, ví dụ như bảng ORDER_LINE_ITEM. Nhưng khi sử dụng Event Sourcing, Order Service lưu trữ một Đơn đặt hàng dưới dạng các event thay đổi trạng thái của nó: Created, Approved, Shipped, Cancelled. Mỗi event chứa đủ dữ liệu để tái tạo lại trạng thái của Đơn đặt hàng.

8

Events được lưu trữ trong Event Store. Event Store cũng hoạt động giống như Message Broker trong các kiến ​​trúc mà chúng tôi đã mô tả trước đó. Nó cung cấp một API cho phép các dịch vụ đăng ký các event. Event Store cung cấp tất cả event cho tất cả người đăng ký quan tâm. Event Store là xương sống của event-driven microservices architecture.

Event sourcing có nhiều lợi ích. Nó giải quyết một trong những vấn đề chính trong việc thực hiện event-driven architecture và làm cho nó có thể xuất bản event đáng tin cậy bất cứ khi nào thay đổi trạng thái. Kết quả là, nó giải quyết các vấn đề nhất quán về dữ liệu trong kiến ​​trúc microservices. Event sourcing cũng cung cấp nhật ký kiểm tra đáng tin cậy 100% về các thay đổi được thực hiện cho một thực thể nghiệp vụ và có thể thực hiện các truy vấn thời gian xác định trạng thái của một thực thể tại bất kỳ thời điểm nào. Điều này làm cho nó dễ dàng hơn nhiều để di chuyển từ một ứng dụng nguyên khối sang microservices.

Event sourcing cũng có một số hạn chế. Đó là một phong cách lập trình khác và không quen thuộc và do đó cần thời gian để học hỏi, nghiên cứu thêm. Bạn phải sử dụng Command Query Responsibility Segregation (CQRS) để thực hiện truy vấn và phải xử lý để đạt được trạng thái dữ liệu nhất quán cuối cùng (eventually consistent data).

Để hiểu rõ hơn về Event Souring và Event-driven delopment, các bạn có thể đọc tại đây: Event-Driven Architecture

Tóm lược

Trong kiến ​​trúc microservices, mỗi service có kho dữ liệu riêng của nó. Các microservices khác nhau có thể sử dụng các cơ sở dữ liệu SQL và NoSQL khác nhau. Trong khi kiến ​​trúc cơ sở dữ liệu này có những lợi ích đáng kể, nó tạo ra một số thách thức quản lý dữ liệu phân tán. Thách thức đầu tiên là làm thế nào để thực hiện các business transactions duy trì tính thống nhất trên nhiều dịch vụ. Thách thức thứ hai là cách triển khai các truy vấn lấy dữ liệu từ nhiều dịch vụ.

Đối với nhiều ứng dụng, giải pháp là sử dụng event-driven architecture. Một thách thức với việc triển khai kiến ​​trúc hướng event là cách cập nhật trạng thái nguyên tố và cách xuất bản các event. Có một vài cách để thực hiện việc này, bao gồm việc sử dụng cơ sở dữ liệu như message queue, transaction log mining, và event sourcing.

Read more:
https://www.nginx.com/blog/event-driven-data-management-microservices/

Đây là loạt bài viết về Microservices mình đã đọc, đúc kết và sưu tầm được trong quá trình tìm hiểu về nó. Hi vọng nó cũng giúp ích được cho bạn trong quá trình architecture design và deploy các ứng dụng microservices.

Tổng hợp và dịch by edwardthienhoang

2 thoughts on “Microservices: Từ Thiết Kế Đến Triển Khai – Phần 5: Event-Driven Data Management”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.