Các mô hình để duy trì tính nhất quán trong một hệ thống nguồn có sự kiện phân tán?


12

Gần đây tôi đã đọc về nguồn cung cấp sự kiện và thực sự thích những ý tưởng đằng sau nó nhưng bị mắc kẹt với vấn đề sau.

Giả sử bạn có N quy trình đồng thời nhận lệnh (ví dụ: máy chủ web), tạo kết quả và lưu trữ chúng trong một cửa hàng tập trung. Chúng ta cũng giả sử rằng tất cả trạng thái ứng dụng nhất thời được duy trì trong bộ nhớ của các quy trình riêng lẻ bằng cách áp dụng tuần tự các sự kiện từ cửa hàng.

Bây giờ, giả sử chúng ta có quy tắc kinh doanh sau: mỗi người dùng riêng biệt phải có một tên người dùng duy nhất.

Nếu hai quy trình nhận lệnh đăng ký người dùng cho cùng một tên người dùng X, cả hai đều kiểm tra xem X không có trong danh sách tên người dùng của họ, quy tắc sẽ xác thực cho cả hai quy trình và cả hai đều lưu trữ sự kiện "người dùng mới với tên người dùng X" trong cửa hàng .

Hiện tại chúng tôi đã bước vào một trạng thái toàn cầu không nhất quán vì quy tắc kinh doanh bị vi phạm (có hai người dùng riêng biệt có cùng tên người dùng).

Trong máy chủ N truyền thống <-> 1 hệ thống kiểu RDBMS, cơ sở dữ liệu được sử dụng làm điểm đồng bộ hóa trung tâm giúp ngăn chặn sự không nhất quán đó.

Câu hỏi của tôi là: làm thế nào để các hệ thống nguồn có sự kiện thường tiếp cận vấn đề này? Có phải họ chỉ đơn giản xử lý mọi lệnh một cách tuần tự (ví dụ: giới hạn số lượng quá trình có thể ghi vào cửa hàng thành 1)?


1
Là hạn chế như vậy được kiểm soát bởi mã hoặc nó là một ràng buộc db? N sự kiện có thể hoặc không thể được gửi đi được xử lý theo trình tự ... N sự kiện có thể đi qua xác nhận đồng thời không loại bỏ nhau. Nếu vấn đề đặt hàng, bạn sẽ cần phải đồng bộ hóa xác nhận. Hoặc để sử dụng hàng đợi để tranh thủ các sự kiện được thực hiện theo tuần tự
Laiv

@Laiv đúng. Để đơn giản, tôi giả sử rằng không có cơ sở dữ liệu, tất cả trạng thái được giữ trong bộ nhớ. Xử lý các loại lệnh cụ thể tuần tự thông qua hàng đợi sẽ là một tùy chọn nhưng có vẻ như nó có thể phức tạp khi quyết định lệnh nào có thể gây ảnh hưởng đến người khác và cuối cùng tôi có thể đặt tất cả các lệnh trong cùng một hàng đợi để có một lệnh xử lý một quy trình : / Ví dụ: nếu tôi có một người dùng thêm bình luận trên một bài đăng trên blog, thì "xóa người dùng", "đình chỉ người dùng", "xóa bài đăng trên blog", "vô hiệu hóa bình luận bài đăng trên blog", v.v.
Olivier Lalonde

1
Tôi đồng ý với bạn, để làm việc với hàng đợi hoặc semaphores là không đơn giản. Không phải để làm việc với các mẫu đồng thời hoặc nguồn sự kiện. Nhưng về cơ bản, tất cả các giải pháp đều kết thúc với lưu lượng truy cập của một sự kiện trong hệ thống. Tuy nhiên, đó là một mô hình thú vị. Ngoài ra còn có các bộ đệm ngoài được định hướng cho các bộ dữ liệu như Redis có thể giúp quản lý lưu lượng này giữa các nút, như lưu trữ trạng thái cuối cùng của một thực thể hoặc nếu thực thể đó đang được xử lý ngay bây giờ. Cache được chia sẻ là khá phổ biến trong loại phát triển này. Nó có vẻ phức tạp nhưng đừng bỏ cuộc ;-) nó khá thú vị
Laiv

Câu trả lời:


6

Trong máy chủ N truyền thống <-> 1 hệ thống kiểu RDBMS, cơ sở dữ liệu được sử dụng làm điểm đồng bộ hóa trung tâm giúp ngăn chặn sự không nhất quán đó.

Trong các hệ thống nguồn có sự kiện, "cửa hàng sự kiện" đóng vai trò tương tự. Đối với một đối tượng có nguồn gốc sự kiện, bài viết của bạn là phần bổ sung các sự kiện mới của bạn vào một phiên bản cụ thể của luồng sự kiện. Vì vậy, giống như với lập trình đồng thời, bạn có thể có được một khóa trong lịch sử đó khi xử lý lệnh. Nó phổ biến hơn cho các hệ thống có nguồn gốc sự kiện để có một cách tiếp cận lạc quan hơn - tải lịch sử trước đó, tính toán lịch sử mới, sau đó so sánh và trao đổi. Nếu một số lệnh khác cũng đã được ghi vào luồng đó, thì việc so sánh và trao đổi của bạn không thành công. Từ đó, bạn có thể chạy lại lệnh của bạn hoặc từ bỏ lệnh của bạn hoặc thậm chí có thể hợp nhất kết quả của bạn vào lịch sử.

Sự tham gia trở thành một vấn đề lớn nếu tất cả các máy chủ N có lệnh M của chúng đang cố ghi vào một luồng. Câu trả lời thông thường ở đây là phân bổ lịch sử cho từng thực thể có nguồn gốc trong mô hình của bạn. Vì vậy, Người dùng (Bob) sẽ có một lịch sử riêng biệt với Người dùng (Alice) và ghi vào một khối sẽ không ghi cho người khác.

Câu hỏi của tôi là: làm thế nào để các hệ thống nguồn có sự kiện thường tiếp cận vấn đề này? Họ chỉ đơn giản là xử lý mọi lệnh tuần tự?

Greg Young về xác nhận hợp lệ

Có một cách thanh lịch để kiểm tra các mâu thuẫn độc đáo trên các thuộc tính đối tượng miền mà không chuyển logic nghiệp vụ vào lớp dịch vụ không?

Trong nhiều trường hợp, câu trả lời ngắn, điều tra yêu cầu đó cho thấy sâu sắc hơn rằng (a) đó là một proxy kém hiểu biết đối với một số yêu cầu khác, hoặc (b) rằng vi phạm "quy tắc" có thể được chấp nhận nếu chúng có thể được phát hiện (báo cáo ngoại lệ) , giảm nhẹ trong một số cửa sổ thời gian hoặc tần số thấp (ví dụ: khách hàng có thể kiểm tra xem tên có khả dụng hay không trước khi gửi lệnh để sử dụng nó).

Trong một số trường hợp, nơi cửa hàng sự kiện của bạn giỏi thiết lập xác thực (ví dụ: cơ sở dữ liệu quan hệ), thì bạn thực hiện yêu cầu bằng cách viết vào bảng "tên duy nhất" trong cùng một giao dịch vẫn tồn tại các sự kiện.

Trong một số trường hợp, bạn chỉ có thể thực thi yêu cầu bằng cách có tất cả các tên người dùng được xuất bản vào cùng một luồng (cho phép bạn đánh giá bộ tên trong bộ nhớ, như một phần của mô hình miền của bạn). - Trong trường hợp này, hai quy trình sẽ cập nhật cố gắng cập nhật "lịch sử luồng", nhưng một trong các hoạt động so sánh và hoán đổi sẽ thất bại và thử lại lệnh đó sẽ có thể phát hiện xung đột.


1) Cảm ơn những lời đề nghị và tài liệu tham khảo. Khi bạn nói "so sánh và trao đổi", bạn có nghĩa là quá trình đó, tại thời điểm lưu trữ một sự kiện, phát hiện các sự kiện mới đã đến kể từ khi nó bắt đầu xử lý lệnh? Tôi đoán điều này sẽ yêu cầu một cửa hàng sự kiện hỗ trợ ngữ nghĩa "so sánh và trao đổi", đúng không? (ví dụ: "chỉ viết sự kiện này và chỉ khi sự kiện cuối cùng có ID X")?
Olivier Lalonde

2) Tôi cũng thích ý tưởng chấp nhận sự không nhất quán tạm thời và sửa chữa chúng cuối cùng nhưng tôi không chắc mình sẽ mã hóa nó theo cách đáng tin cậy như thế nào ... có thể có một quy trình chuyên dụng xác nhận tuần tự các sự kiện và tạo ra các sự kiện rollback khi phát hiện có gì sai Cảm ơn!
Olivier Lalonde

(1) Tôi sẽ nói "phiên bản mới của lịch sử" chứ không phải là "sự kiện mới", nhưng bạn có ý tưởng; chỉ thay thế lịch sử nếu nó là một trong những chúng ta đang mong đợi.
VoiceOfUnreason

(2) Yup. Đó là một chút logic đọc các sự kiện từ cửa hàng theo đợt và vào cuối đợt phát sóng một báo cáo ngoại lệ ("chúng tôi có quá nhiều người dùng tên là Bob") hoặc gửi các lệnh để bù cho sự cố (giả sử phản hồi đúng là tính toán mà không cần sự can thiệp của con người).
VoiceOfUnreason

2

Âm thanh như bạn có thể thực hiện một quy trình kinh doanh ( sagatrong bối cảnh Domain Driven Design) cho đăng ký người dùng nơi người dùng được đối xử như một CRDT.

Tài nguyên

  1. https://doc.akka.io/docs/akka/c Hiện / distribution-data.html http://archive.is/t0QIx

  2. "CRDTs với Akka Distributed Data" https://www.slideshare.net/markusjura/crdts-with-akka-distributed-data để tìm hiểu về

    • CmRDTs - CRDT dựa trên hoạt động
    • CvRDTs - CRTD dựa trên nhà nước
  3. Ví dụ mã trong Scala https://github.com/akka/akka-samples/tree/master/akka-sample-distribution-data-scala . Có lẽ "giỏ hàng" là phù hợp nhất.

  4. Tham quan cụm Akka - Dữ liệu phân tán của Akka https://manuel.bernhardt.io/2018/01/03/tour-akka-cluster-akka-distribution-data/
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.