Dịch vụ vi mô: Xử lý tính nhất quán cuối cùng


22

Giả sử chúng ta có chức năng cập nhật mật khẩu của Người dùng.

Khi nhấp vào nút 'Cập nhật mật khẩu', UpdatePasswordEvent sẽ được gửi đến một chủ đề trong đó có 3 dịch vụ khác được đăng ký:

  1. Một dịch vụ thực sự cập nhật mật khẩu của Người dùng
  2. Dịch vụ cập nhật lịch sử mật khẩu của người dùng
  3. Một dịch vụ gửi e-mail thông báo cho người dùng rằng mật khẩu của anh ta đã bị thay đổi.

Dựa trên những gì tôi đã hiểu về tính nhất quán cuối cùng, tất cả các dịch vụ này (người tiêu dùng) sẽ nhận được sự kiện cùng một lúc và xử lý chúng một cách riêng biệt, trong một kịch bản tốt, sẽ dẫn đến dữ liệu nhất quán.

Tuy nhiên, nếu một dịch vụ không xử lý sự kiện thì sao? ví dụ ngắt kết nối đột ngột, lỗi cơ sở dữ liệu, v.v ... Mô hình / thực tiễn tốt để xử lý các lỗi giao dịch này là gì?

Tôi đã nghĩ đến việc tạo một RollbackTopic trong đó nếu bất kỳ sự kiện nào không được xử lý, một RollbackEvent sẽ được tạo trong một chủ đề trong đó "dịch vụ rollback" sẽ thực hiện công việc đó và hoàn nguyên dữ liệu


11
Bạn không thể hoàn tác một email đã gửi :-)
Laiv

2
Bởi vì tất cả chúng nên là một phần của cùng một dịch vụ. Dịch vụ vi mô trái ngược với nguyên khối, điều đó không có nghĩa là bạn phải thiết kế chúng ít nhất có thể. Mặc dù điều này không liên quan trực tiếp, nhưng bạn nên đọc câu hỏi này và hai câu trả lời hàng đầu: softwareengineering.stackexchange.com/questions/339230/
Thẻ

1
Bạn có thể xem xét cập nhật mật khẩu của người dùng trong cơ sở dữ liệu một cách đồng bộ, để bạn cung cấp phản hồi ngay lập tức cho người dùng và kích hoạt các dịch vụ khác một cách không đồng bộ bằng cách phát ra một thông báo rằng mật khẩu đã thay đổi trên một chủ đề, để tin nhắn của bạn không phải chứa mật khẩu.
cr3

Là e-mail để nói với người dùng rằng giao dịch đã hoàn thành, hay là ở đó để nói với người dùng rằng ai đó (hy vọng họ) đã thay đổi mật khẩu. Nếu không phải là bạn, thì bạn cần phải hành động. Nếu thứ 2 thì chỉ cần gửi e-mail ngay bây giờ, tốt nhất bạn có thể.
ctrl-alt-delor

Câu trả lời:


29

Dựa trên những gì tôi đã hiểu về tính nhất quán cuối cùng, tất cả các dịch vụ này (người tiêu dùng) sẽ nhận được sự kiện cùng một lúc và xử lý chúng một cách riêng biệt , trong một kịch bản tốt, sẽ dẫn đến dữ liệu nhất quán.

Không, không nhất thiết. Như tôi đã nhận xét, chúng tôi không thể hoàn tác một email đã gửi, vì vậy chúng tôi vẫn cần một loại "trình tự". IPC về quản lý dữ liệu theo sự kiện không được miễn trừ cho việc trồng cây 1 .

Chẳng hạn, email không nên được gửi trừ khi các giao dịch trước đó kết thúc thành công và dịch vụ email có bằng chứng về nó. 3

Tuy nhiên, nếu một dịch vụ không xử lý sự kiện thì sao? ví dụ ngắt kết nối đột ngột, lỗi cơ sở dữ liệu, v.v ... Mô hình / thực tiễn tốt để xử lý các lỗi giao dịch này là gì?

Nói xin chào với những ngụy biện của máy tính phân tán . Chúng là thứ khiến mọi thứ trở nên phức tạp và như thường lệ, không có viên đạn bạc nào để đối phó với chúng.

Trước khi bắt đầu hành trình tìm kiếm Lost Ark, chúng tôi phải xem xét yêu cầu tổ chức trước. Thông thường, giải pháp là cách tổ chức đối mặt với những vấn đề này trong thế giới thực .

Mọi người (bộ phận) làm gì khi dữ liệu nhất định bị thiếu hoặc không đầy đủ?

Chúng ta sẽ nhận ra rằng các phòng ban khác nhau có các giải pháp khác nhau, những người, hoàn toàn, bao gồm giải pháp được thực hiện.

Dù sao, ở đây một số thực tiễn có thể giúp chúng tôi ra chiến lược để làm theo.

Sự thống nhất cuối cùng

Thay vì đảm bảo rằng hệ thống luôn ở trạng thái nhất quán, thay vào đó chúng ta có thể chấp nhận rằng hệ thống sẽ có được nó vào một lúc nào đó trong tương lai. Cách tiếp cận này đặc biệt hữu ích cho các hoạt động kinh doanh lâu dài.

Cách để hệ thống đạt được sự thống nhất khác nhau tùy theo hệ thống. Nó có thể liên quan từ các quy trình tự động đến một số loại can thiệp của con người. Chẳng hạn, việc thử lại điển hình sau đó hoặc liên hệ với Dịch vụ khách hàng .

Hủy bỏ tất cả các hoạt động

Đặt hệ thống trở lại trạng thái nhất quán thông qua các giao dịch bù . Tuy nhiên, chúng ta phải tính đến việc, các giao dịch này cũng có thể thất bại, điều gì có thể dẫn chúng ta đến một điểm mà sự không nhất quán thậm chí còn khó giải quyết hơn. Và, một lần nữa, chúng tôi không thể hoàn tác một email đã gửi.

Đối với số lượng giao dịch thấp, cách tiếp cận này là khả thi, vì số lượng giao dịch bù cũng quá thấp. Nếu có một số giao dịch kinh doanh liên quan đến IPC, việc xử lý một giao dịch bù cho mỗi giao dịch sẽ là một thách thức.

Nếu chúng ta thực hiện các giao dịch bù , chúng ta sẽ thấy mẫu thiết kế bộ ngắt mạch rất hữu ích - và tôi bắt buộc phải nói -

Giao dịch phân tán

Ý tưởng là mở rộng nhiều giao dịch trong một giao dịch, thông qua quy trình quản lý tổng thể được gọi là Trình quản lý giao dịch . Một thuật toán phổ biến để xử lý các giao dịch phân tán là Cam kết hai pha .

Mối quan tâm chính của các giao dịch phân tán là chúng dựa vào việc khóa tài nguyên trong suốt vòng đời của nó và như chúng ta biết, mọi thứ cũng có thể sai đối với Trình quản lý giao dịch .

Nếu Người quản lý giao dịch bị xâm phạm, chúng ta có thể kết thúc với một số khóa tất cả các bối cảnh bị ràng buộc khác nhau, dẫn đến các hành vi không mong muốn do việc gửi tin nhắn. 2

Hoạt động phân hủy. Tại sao?

Nếu bạn đang phân tách một hệ thống hiện có và tìm thấy một tập hợp các khái niệm thực sự muốn nằm trong một ranh giới giao dịch duy nhất, có lẽ hãy để chúng tồn tại đến cuối cùng.

Sam Newman

Để phù hợp với các lập luận trên, Sam -in trong cuốn sách Building microservice của mình - nói rằng, nếu chúng ta thực sự, thực sự không thể đủ khả năng thống nhất cuối cùng, chúng ta nên tránh chia tách hoạt động ngay bây giờ.

Nếu chúng tôi không đủ khả năng chia một số hoạt động nhất định thành hai hoặc nhiều giao dịch, có thể nói rằng - có thể - các giao dịch này thuộc cùng một bối cảnh bị ràng buộc, hoặc - ít nhất là - trong bối cảnh xuyên suốt vẫn được mô hình hóa.

Ví dụ: trong trường hợp của chúng tôi, chúng tôi nhận ra rằng các giao dịch # 1 và # 2 liên quan chặt chẽ với nhau và có lẽ cả hai có thể thuộc cùng một Tài khoản bối cảnh , Người dùng , Đăng ký , bất cứ điều gì ...

Xem xét đặt cả hai hoạt động trong ranh giới của cùng một giao dịch. Nó sẽ làm cho toàn bộ hoạt động dễ dàng hơn để xử lý. Ngoài ra, mức độ quan trọng của từng giao dịch. Có lẽ, nếu giao dịch số 2 thất bại, nó sẽ không thỏa hiệp toàn bộ hoạt động. Trong trường hợp nghi ngờ yêu cầu tổ chức .


1: Không phải kiểu dàn nhạc mà bạn nghĩ. Tôi không nói về việc trồng cây của ESB. Tôi đang nói về việc làm cho các dịch vụ phản ứng với sự kiện thích hợp.

2: Bạn có thể tìm thấy ý kiến thú vị của Sam Newman về các giao dịch phân tán.

3: Hãy xem câu trả lời của David Parker về chủ đề này.


3
Câu trả lời rất hay. Tôi chỉ nhấn mạnh tầm quan trọng của việc tính đến các rủi ro khi sử dụng các giao dịch phân tán - chủ yếu là khóa tài nguyên tạo ra các bế tắc và tạm dừng hệ thống. Trên một sản phẩm thương mại điện tử tôi đã làm việc khoảng 3 năm trước, chúng tôi phải thay thế DT bằng hệ thống nhắn tin, bởi vì với lượng người dùng có sẵn trong hệ thống, hệ thống rất dễ bị lỗi. Các vấn đề với DT chủ yếu xảy ra khi cơ sở người dùng phát triển.
Andy

7

Trong trường hợp của bạn, bạn không thể xử lý cả ba thứ cùng một lúc. Những gì bạn cần là một quá trình. Đây là một ví dụ cực kỳ đơn giản:

Chỉ huy và tổ chức sự kiện

Điều quan trọng cần biết là các hoạt động thay đổi trạng thái PHẢI luôn luôn được thực hiện trên một thực thể nhất quán. Trừ khi bạn có thể đảm bảo tính nhất quán mạnh mẽ , nó phải được thực hiện trên hồ sơ chính.

Hệ thống của bạn phải đảm bảo rằng trước khi bất kỳ sự kiện nào được nêu ra trong các thay đổi hệ thống của bạn, trước tiên, bạn phải được duy trì an toàn giao dịch. Điều này là để đảm bảo rằng một sự kiện lớn lên thực sự là một sự xác nhận về những gì thực sự đã xảy ra.

Có một số phần khó khăn của quy trình và tôi sẽ bỏ qua những phần rõ ràng - chẳng hạn như: Điều gì xảy ra nếu máy chủ cơ sở dữ liệu của bạn chết khi duy trì người dùng có mật khẩu thay đổi? Bạn chỉ cần phát hành UpdatePassword một lần nữa. Tuy nhiên, một số phần cần được bạn quan tâm và đó là:

  • xử lý trùng lặp tin nhắn,
  • xử lý gửi e-mail.

Trong một hệ thống, bộ điều phối quy trình (PO) không gì khác chính là một thực thể khác, chứa trạng thái bên trong - theo nghĩa đen - và cho phép chuyển đổi giữa các trạng thái, hoạt động hiệu quả như một loại máy trạng thái. Nhờ trạng thái nội bộ, bạn có thể loại bỏ xử lý trùng lặp thư.

Khi PO ở Newtrạng thái và xử lý UserPasswordHasBeenUpdated, nó sẽ thay đổi trạng thái thành UserPasswordHasBeenUpdated(hoặc bất kỳ tên trạng thái nào phù hợp với bạn). Nếu PO vẫn ở trong một UserPasswordHasBeenUpdatedvà một cái khác UserPasswordHasBeenUpdatedsẽ đến, PO sẽ hoàn toàn bỏ qua tin nhắn, vì biết đó là một bản sao. Cơ chế tương tự cũng sẽ được thực hiện cho các tiểu bang khác.

Xử lý việc gửi e-mail thực tế phức tạp hơn một chút. Ở đây bạn có hai lựa chọn:

  1. gửi nó nhiều nhất một lần,
  2. gửi nó ít nhất một lần.

Gửi nó nhiều nhất một lần

Với tùy chọn này, khi PO đạt đến UserPasswordHistoryHasBeenSavedtrạng thái, lệnh gửi e-mail sẽ được gửi đi như một phản ứng đối với thay đổi trạng thái. Hệ thống của bạn sẽ đảm bảo UserPasswordHistoryHasBeenSavedtrạng thái sẽ được duy trì trước khi gửi e-mail, tức là tin nhắn trùng lặp sẽ không kích hoạt e-mail gửi lại. Với phương pháp này, bạn đảm bảo rằng trạng thái chính xác được lưu cho PO nhưng không thể đảm bảo bất kỳ thao tác nào sau đây.

Gửi ít nhất một lần

Đây là những gì tôi sẽ đi cho.

Thay vì lưu UserPasswordHistoryHasBeenSavedvà gửi e-mail như một phản ứng với nó, bạn hãy thử gửi e-mail trước. Nếu thao tác gửi thất bại, trạng thái của PO không bao giờ được thay đổi thành UserPasswordHistoryHasBeenSavedvà một thông báo khác cùng loại vẫn được xử lý. Nếu việc gửi e-mail thực sự thành công nhưng hệ thống của bạn sẽ thất bại trong khi duy trì PO với UserPasswordHistoryHasBeenSavedtrạng thái mới , một thông báo khác UserPasswordHistoryHasBeenSavedsẽ một lần nữa kích hoạt lệnh gửi e-mail và người dùng sẽ nhận được nhiều lần .

Trong trường hợp của bạn, bạn muốn chắc chắn rằng người dùng thực sự nhận được e-mail. Đó là lý do tại sao tôi sẽ chọn các tùy chọn thứ hai so với đầu tiên.


2

Hệ thống xếp hàng không hoàn toàn mong manh như bạn nghĩ.

Nếu chúng tôi đã viết cả ba quy trình cho một db quan hệ, chúng tôi có thể sử dụng một giao dịch để xử lý lỗi giữa các quy trình.

Nếu không có cam kết cuối cùng, công việc một phần sẽ bị loại bỏ.

Trong một hệ thống cơ sở hàng đợi, bạn sẽ có một tùy chọn tương tự khi bạn đọc một tin nhắn từ hàng đợi để xử lý các lỗi giữa quy trình.

Ví dụ, Amazon SQS chỉ ẩn các tin nhắn được đọc. trừ khi lệnh Xóa cuối cùng được gửi, tin nhắn sẽ xuất hiện lại hoặc được đưa vào hàng đợi thư chết.

Bạn có thể thực hiện các 'giao dịch' tương tự theo nhiều cách khác nhau, về cơ bản là giữ một bản sao của tin nhắn cho đến khi bạn nhận được xác nhận xử lý thành công. Nếu xác nhận không được nhận kịp thời. bạn có thể gửi tin nhắn một lần nữa hoặc giữ nó để chú ý thủ công.

Có khả năng bạn có thể tạo một 'dịch vụ rollback' để theo dõi các tin nhắn bị lỗi này, biết về các tin nhắn liên quan và trạng thái trong quá khứ và thực hiện khôi phục.

Tuy nhiên! Nó thường là tốt hơn chỉ để gửi lại các tin nhắn bị lỗi. Sau khi tất cả những xu hướng là trường hợp cạnh. Hoặc là một máy chủ bị lỗi nghiêm trọng hoặc có lỗi trong việc xử lý một loại thông báo cụ thể.

Sau khi thông báo lỗi, dịch vụ có thể được sửa chữa và các tin nhắn được xử lý thành công. Đưa toàn bộ hệ thống trở lại trạng thái nhất quán.


2

Những gì bạn đang đối mặt ở đây là vấn đề hai tướng . Về bản chất: làm thế nào bạn có thể chắc chắn nhận được một tin nhắn và phản hồi cho tin nhắn đó xảy ra? Trong nhiều trường hợp, một giải pháp hoàn hảo không tồn tại. Trong thực tế, trong một hệ thống phân tán, thường không thể nhận được một lần gửi tin nhắn chính xác .

Một nhận xét rõ ràng đầu tiên là dịch vụ thay đổi mật khẩu sẽ gửi sự kiện thay đổi mật khẩu. Bằng cách này, lịch sử mật khẩu và dịch vụ gửi thư chỉ được kích hoạt khi mật khẩu thực sự thay đổi, bất kể tại sao nó thay đổi.

Để thực sự giải quyết vấn đề của bạn, tôi sẽ không xem xét các giao dịch phân tán, mà thay vào đó hãy nhìn theo hướng gửi tin nhắn ít nhất một lần và xử lý tạm thời.

  • Ít nhất một lần

    Để đảm bảo sự kiện thay đổi mật khẩu thực sự được nhìn thấy bởi tất cả người tiêu dùng, bạn cần sử dụng kênh liên lạc lâu bền, nơi các tin nhắn có thể được sử dụng theo kiểu "ít nhất một lần". Người tiêu dùng chỉ thừa nhận một thông điệp là được tiêu thụ khi họ đã xử lý đầy đủ. Ví dụ, nếu dịch vụ lịch sử mật khẩu gặp sự cố trong khi viết mục lịch sử, nó sẽ đọc lại cùng một sự kiện thay đổi mật khẩu sau khi khởi động lại và thử lại, thừa nhận sự kiện đó là chỉ đọc sau khi nó được ghi vào lịch sử. Bạn nên chọn giải pháp xếp hàng tin nhắn dựa trên khả năng gửi lại tin nhắn cho đến khi chúng được xác nhận.

  • Vô tư

    Sau khi đạt được giao hàng ít nhất một lần, có vấn đề về các hành động trùng lặp xảy ra khi một tin nhắn được xử lý một phần trước khi người tiêu dùng bị gián đoạn và sau đó xử lý lại sau đó. Điều đó nên được giải quyết bằng cách thiết kế từng dịch vụ sao cho nó là bình thường. Việc ghi mà nó thực hiện có thể xảy ra nhiều lần mà không có tác dụng phụ, hoặc nó giữ cho kho lưu trữ của riêng mình về những hành động đã thực hiện và tránh thực hiện một hành động nhiều lần. Trong trường hợp gửi thư, bạn sẽ thấy có lẽ không đáng để cố gắng để nó hoạt động bình thường và vẫn ổn khi thỉnh thoảng một thư được gửi hai lần.

Trong mọi trường hợp, hãy cẩn thận cách bạn thực hiện các dịch vụ của mình. Dịch vụ lịch sử mật khẩu của bạn có thực sự cần phải độc lập với dịch vụ thay đổi mật khẩu không?


1

Tôi không đồng ý với rất nhiều câu trả lời.

  1. Gửi e-mail ngay bây giờ Người nào đó đã thay đổi mật khẩu của bạn. Nếu đó là bạn thì bạn không cần phải làm gì cả. Nếu không hoảng loạn. Điều này sẽ đến khi nó đến.
  2. Thay đổi mật khẩu. Mặc dù bạn có sự nhất quán cuối cùng. Bạn muốn đảm bảo rằng phiên này nhìn thấy những thay đổi được thực hiện bởi người dùng.

Có những lời hứa nhất quán khác mà bạn có thể thêm.

  • Đảm bảo rằng những thay đổi xảy ra theo thứ tự thời gian.
  • Đảm bảo rằng người dùng không bao giờ thấy quay lại, nhưng những người dùng khác vẫn có thể không thấy thay đổi.
  • Co nhung nguoi khac

Những thống nhất bổ sung này sẽ cần phải được thực hiện tùy thuộc vào hành động của ứng dụng.


Tôi không biết ý của bạn là gì khi cập nhật lịch sử, nhưng xin đừng bao giờ thay đổi lịch sử. Nếu bạn chỉ mở rộng DAG, thì điều này sẽ gây ra sự thay đổi trong trạng thái hiện tại. Họ không độc lập. Nếu họ là như vậy thì bạn không thể dựa vào lịch sử phản ánh những gì đã xảy ra. (và cuối cùng nhưng không kém phần quan trọng, đừng lưu trữ mật khẩu, xem làm thế nào để không lưu trữ mật khẩu )


Nếu bạn có thể gửi email ngay từ đầu thì cách tiếp cận của bạn vẫn ổn. Nếu bạn phải gửi một cái gì đó cùng với email. Có thể một loại liên kết / dữ liệu chỉ có thể có được sau khi đạt được sự thống nhất, sau đó bạn không thể gửi email trước. Đó là những gì tôi nhận xét là consider asking the organization first.. Bạn có khả năng đúng. Tuy nhiên, tôi thấy điều quan trọng là phải xác định những sự kiện mà chúng ta không thể hoàn tác. Ví dụ thông báo cho người dùng cuối. Thông báo nằm trên trạng thái thực của dữ liệu người dùng có thể gây ấn tượng xấu.
Laiv

Điều đó nói rằng, đối với kịch bản cụ thể này (thông báo thay đổi mật khẩu), tôi đã đồng ý với phương pháp này. Ngay khi nó đặt ra các yêu cầu.
Laiv
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.