Chiến lược giao dịch được chấp nhận nhất cho microservice là gì


80

Một trong những vấn đề chính mà tôi đã thấy xảy ra trong một hệ thống với microservice là cách các giao dịch hoạt động khi chúng trải rộng trên các dịch vụ khác nhau. Trong kiến ​​trúc riêng của chúng tôi, chúng tôi đã sử dụng các giao dịch phân tán để giải quyết vấn đề này, nhưng chúng đi kèm với các vấn đề của riêng họ. Đặc biệt là bế tắc cho đến nay là một nỗi đau.

Một tùy chọn khác có vẻ là một loại trình quản lý giao dịch được thực hiện tùy chỉnh, biết các luồng trong hệ thống của bạn và sẽ xử lý các rollback cho bạn như một quá trình nền trải rộng trên toàn bộ hệ thống của bạn (vì vậy nó sẽ báo cho các dịch vụ khác quay lại và nếu họ không hoạt động, hãy thông báo cho họ sau).

Có một lựa chọn khác, được chấp nhận? Cả hai điều này dường như có nhược điểm của họ. Cái đầu tiên có thể gây ra bế tắc và một loạt các vấn đề khác, cái thứ hai có thể dẫn đến sự không thống nhất dữ liệu. Có những lựa chọn tốt hơn?


Để chắc chắn, những dịch vụ siêu nhỏ đó được sử dụng bởi nhiều khách hàng cùng một lúc, hay chỉ một lần tại một thời điểm?
Marcel

2
Tôi đã cố hỏi câu hỏi bất khả tri này, Marcel. Nhưng hãy giả sử rằng chúng tôi đang sử dụng một hệ thống đủ lớn để thực hiện cả hai điều đó và chúng tôi muốn có một kiến ​​trúc hỗ trợ cả hai.
Kristof

4
Đây là một câu hỏi kém, nhưng rất quan trọng. Khi hầu hết mọi người nghĩ về "giao dịch", họ nghĩ hầu như chỉ về tính nhất quán và không phải là khía cạnh nguyên tử, sự cô lập hay độ bền của giao dịch. Câu hỏi thực sự nên được nêu "Làm thế nào để bạn tạo ra một hệ thống tuân thủ ACID với kiến ​​trúc microservice. Chỉ thực hiện tính nhất quán và không phải phần còn lại của ACID là không thực sự hữu ích. Trừ khi bạn thích dữ liệu xấu.
Jeff Fischer

10
Bạn luôn có thể cố gắng thay đổi câu hỏi của tôi để làm cho nó bớt "kém chữ", Jeff Fischer.
Kristof

Câu hỏi và thảo luận này liên quan chặt chẽ với câu hỏi khác này về SO .
Paulo Merson

Câu trả lời:


42

Cách tiếp cận thông thường là cô lập các dịch vụ siêu nhỏ đó càng nhiều càng tốt - coi chúng là các đơn vị đơn lẻ. Sau đó, các giao dịch có thể được phát triển trong toàn bộ dịch vụ (nghĩa là không phải là một phần của giao dịch DB thông thường, mặc dù bạn vẫn có thể có các giao dịch DB bên trong dịch vụ).

Hãy nghĩ xem các giao dịch xảy ra như thế nào và có ý nghĩa gì đối với các dịch vụ của bạn sau đó, bạn có thể thực hiện cơ chế khôi phục mà không thực hiện hoạt động ban đầu hoặc hệ thống cam kết 2 pha bảo lưu hoạt động ban đầu cho đến khi được cam kết thực sự. Tất nhiên cả hai hệ thống này đều có nghĩa là bạn đang tự triển khai, nhưng sau đó bạn đã triển khai các dịch vụ siêu nhỏ của mình.

Các dịch vụ tài chính luôn làm điều này mọi lúc - nếu tôi muốn chuyển tiền từ ngân hàng của mình sang ngân hàng của bạn, không có giao dịch nào giống như bạn có trong DB. Bạn không biết hệ thống nào ngân hàng đang chạy, do đó phải đối xử hiệu quả với từng hệ thống như microservice của bạn. Trong trường hợp này, ngân hàng của tôi sẽ chuyển tiền của tôi từ tài khoản của tôi sang tài khoản đang nắm giữ và sau đó nói với ngân hàng của bạn rằng họ có một số tiền, nếu gửi không thành công, ngân hàng của tôi sẽ hoàn trả lại tài khoản của tôi bằng số tiền họ đã cố gắng gửi.


5
Bạn đang cho rằng hoàn trả không bao giờ có thể thất bại.
Sanjeev Kumar Dangi

9
@SanjeevKumarDangi ít có khả năng và ngay cả khi nó không thành công, nó có thể được xử lý dễ dàng vì tài khoản đang nắm giữ và tài khoản thực tế nằm trong tầm kiểm soát của ngân hàng.
gldraphael

30

Tôi nghĩ rằng sự khôn ngoan tiêu chuẩn là không bao giờ có giao dịch vượt qua ranh giới microservice. Nếu bất kỳ tập hợp dữ liệu nhất định nào thực sự cần phải phù hợp về mặt nguyên tử với dữ liệu khác, hai thứ đó thuộc về nhau.

Đây là một trong những lý do rất khó để phân chia một hệ thống thành các dịch vụ cho đến khi bạn đã thiết kế đầy đủ. Mà trong thế giới hiện đại có lẽ có nghĩa là viết nó ...


37
Cách tiếp cận như vậy rất có thể dẫn đến việc hợp nhất tất cả các dịch vụ siêu nhỏ vào một ứng dụng nguyên khối duy nhất cuối cùng.
Slava Fomin II

4
Đây là cách tiếp cận chính xác. Nó thực sự đơn giản: nếu bạn cần một giao dịch giữa các dịch vụ, dịch vụ của bạn đã sai: thiết kế lại chúng! @SlavaFominII những gì bạn nói chỉ đúng nếu bạn không biết cách thiết kế một hệ thống microservice. Nếu bạn thấy mình chiến đấu với phương pháp microservice, đừng làm điều đó, khối nguyên khối của bạn sẽ tốt hơn một thiết kế microservice xấu. Chỉ khi bạn tìm thấy ranh giới dịch vụ phù hợp là khi bạn nên phân chia nguyên khối trong các dịch vụ. Mặt khác, sử dụng microservice không phải là lựa chọn kiến ​​trúc phù hợp, nó chỉ tuân theo sự cường điệu.
Francesc Castells

@FrancescCastells Điều gì xảy ra nếu dịch vụ của chúng tôi thực sự yêu cầu giao dịch giữa các dịch vụ khác, bạn có nghĩa là chúng ta nên bỏ qua bối cảnh bị ràng buộc và mô hình hóa các dịch vụ của mình theo cách kết thúc như một giao dịch đơn lẻ? Tôi là một người mới trong microservice, vẫn đang đọc nên tha thứ cho câu hỏi của tôi nếu nó nghe có vẻ ngây thơ ....: D: D
Bilbo Baggins

@BilboBaggins Ý tôi là trái ngược với việc bỏ qua các bối cảnh bị ràng buộc. Theo định nghĩa các giao dịch xảy ra trong một bối cảnh bị ràng buộc và không phải qua một vài trong số chúng. Do đó, nếu bạn thiết kế chính xác các dịch vụ siêu nhỏ của mình cùng với bối cảnh bị ràng buộc, thì các giao dịch của bạn sẽ không mở rộng nhiều dịch vụ. Lưu ý rằng, đôi khi, những gì bạn cần không phải là giao dịch, nhưng xử lý tốt hơn tính nhất quán cuối cùng và hành động bù phù hợp khi mọi thứ không diễn ra chính xác.
Francesc Castells

Tôi không nhận được quan điểm của bạn, có cơ hội nào để chúng ta có thể thảo luận điều này trong một cuộc trò chuyện không?
Bagboins Bilbo

17

Tôi nghĩ rằng nếu tính nhất quán là một yêu cầu mạnh mẽ trong ứng dụng của bạn, bạn nên tự hỏi mình nếu microservice là phương pháp tốt hơn. Giống như Martin Fowler nói :

Microservice giới thiệu các vấn đề nhất quán cuối cùng vì sự nhấn mạnh đáng khen ngợi của họ về quản lý dữ liệu phi tập trung. Với một khối nguyên khối, bạn có thể cập nhật một loạt các thứ với nhau trong một giao dịch. Microservice yêu cầu nhiều tài nguyên để cập nhật và các giao dịch phân tán được tán thành (vì lý do chính đáng). Vì vậy, bây giờ, các nhà phát triển cần phải nhận thức được các vấn đề về tính nhất quán và tìm ra cách phát hiện khi mọi thứ không đồng bộ trước khi làm bất cứ điều gì mà mã sẽ hối tiếc.

Nhưng có lẽ trong trường hợp của bạn, bạn có thể hy sinh tính nhất quán trong trạng thái sẵn có

Các quy trình kinh doanh thường khoan dung hơn về sự không nhất quán so với bạn nghĩ bởi vì các doanh nghiệp thường có sẵn giải thưởng nhiều hơn.

Tuy nhiên, tôi cũng tự hỏi mình có tồn tại một chiến lược cho các giao dịch phân tán trong microservice không, nhưng có lẽ chi phí quá cao. Tôi muốn đưa cho bạn hai xu của tôi với bài báo luôn xuất sắc của Martin Fowler và định lý CAP .


1
Trong các giao dịch kinh doanh phân tán, tính nhất quán đôi khi được giải quyết bằng cách thêm một lớp phối hợp như công cụ quy trình công việc hoặc hàng đợi được thiết kế cẩn thận. Lớp đó xử lý tất cả hai pha cam kết và khôi phục và cho phép các dịch vụ siêu nhỏ tập trung vào logic kinh doanh cụ thể. Nhưng trở lại với CAP, đầu tư vào tính sẵn sàng và nhất quán làm cho hiệu suất trở thành nạn nhân. Làm thế nào để microservice so sánh với rất nhiều lớp tách rời bao gồm doanh nghiệp của bạn trong OOP?
Greg

Bạn có thể sử dụng RAFT thông qua microservice để giải quyết tính nhất quán FWIW
f0ster

1
+1. Tôi thường tự hỏi tại sao trong một giao dịch bối cảnh microservice lại mong muốn tất cả, nếu tất cả các chế độ xem 'kéo' dữ liệu có thể được thực hiện dưới dạng các khung nhìn cụ thể hóa. Ví dụ: nếu một microservice thực hiện ghi nợ từ một tài khoản này và một khoản tín dụng khác cho một tài khoản khác, thì các quan điểm về số dư sẽ chỉ xem xét các cặp tín dụng và ghi nợ, trong đó các khoản tín dụng và ghi nợ chưa từng có sẽ nằm trong bộ đệm cho chế độ xem được cụ thể hóa.
Sentinel

16

Như được đề xuất trong ít nhất một trong những câu trả lời ở đây nhưng cũng có ở những nơi khác trên web, có thể thiết kế một dịch vụ siêu nhỏ để duy trì các thực thể với nhau trong một giao dịch bình thường nếu bạn cần sự thống nhất giữa hai thực thể.

Nhưng đồng thời, bạn cũng có thể gặp phải trường hợp các thực thể không thuộc cùng một dịch vụ siêu nhỏ, ví dụ: hồ sơ bán hàng và hồ sơ đặt hàng (khi bạn đặt hàng để hoàn thành việc bán hàng). Trong những trường hợp như vậy, bạn cũng có thể yêu cầu một cách để đảm bảo tính thống nhất giữa hai dịch vụ siêu nhỏ.

Các giao dịch phân phối truyền thống đã được sử dụng và theo kinh nghiệm của tôi, chúng hoạt động tốt cho đến khi chúng mở rộng đến một kích thước mà việc khóa trở thành một vấn đề. Bạn có thể thư giãn khóa để thực sự chỉ có các tài nguyên có liên quan (ví dụ: mặt hàng đang được bán) bị "khóa" bằng cách sử dụng thay đổi trạng thái, nhưng đây là lúc nó bắt đầu gặp khó khăn vì bạn đang vào lãnh thổ nơi bạn cần xây dựng tất cả logic để làm điều này, bản thân bạn, thay vì có, nói rằng một cơ sở dữ liệu xử lý nó cho bạn.

Tôi đã làm việc với các công ty đã đi theo con đường xây dựng khung giao dịch của riêng họ để xử lý vấn đề phức tạp này, nhưng tôi không khuyên bạn vì nó tốn kém và mất thời gian để trưởng thành.

Có những sản phẩm ngoài kia có thể được bắt vít vào hệ thống của bạn, đảm bảo tính nhất quán. Một công cụ xử lý nghiệp vụ là một ví dụ tốt và họ thường xử lý tính nhất quán cuối cùng và bằng cách sử dụng bù. Các sản phẩm khác hoạt động theo cách tương tự. Bạn thường kết thúc với một lớp phần mềm gần (các) máy khách, liên quan đến tính nhất quán và các giao dịch và dịch vụ cuộc gọi (vi mô) để thực hiện xử lý nghiệp vụ thực tế . Một sản phẩm như vậy là một trình kết nối JCA chung có thể được sử dụng với các giải pháp Java EE (để minh bạch: Tôi là tác giả). Xem http://blog.maxant.co.uk/pebble/2015/08/04/1438716480000.html để biết thêm chi tiết và thảo luận sâu hơn về các vấn đề được nêu ra ở đây.

Một cách khác để xử lý các giao dịch và tính nhất quán là kết hợp một cuộc gọi đến microservice thành một cuộc gọi đến một giao dịch nào đó như một hàng đợi tin nhắn. Lấy ví dụ về hồ sơ bán hàng / hồ sơ đặt hàng từ phía trên - bạn có thể chỉ cần để microservice bán hàng gửi tin nhắn đến hệ thống đặt hàng, được cam kết trong cùng một giao dịch ghi việc bán hàng vào cơ sở dữ liệu. Kết quả là một giải pháp không đồng bộ có tỷ lệ rất tốt. Sử dụng các công nghệ như ổ cắm web, bạn thậm chí có thể giải quyết vấn đề chặn thường liên quan đến việc nhân rộng các giải pháp không đồng bộ. Để biết thêm ý tưởng về các mẫu như thế này, hãy xem một bài viết khác của tôi: http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.html .

Bất kỳ giải pháp nào bạn kết thúc việc chọn, điều quan trọng là phải nhận ra rằng chỉ một phần nhỏ trong hệ thống của bạn sẽ viết những thứ cần nhất quán - hầu hết quyền truy cập có thể chỉ đọc. Vì lý do đó, chỉ xây dựng quản lý giao dịch vào các phần có liên quan của hệ thống, để nó vẫn có thể mở rộng quy mô.


Trong lúc này, tôi muốn nói rằng người ta nên nghiêm túc xem xét việc biến quy trình thành một quy trình không đồng bộ, trong đó mỗi bước trong quy trình là giao dịch đầy đủ. Xem tại đây để biết chi tiết: blog.maxant.co.uk/pebble/2018/02/18/1518974314273.html
Ant Kutschera

"Tạo ví dụ về hồ sơ bán hàng / hồ sơ đặt hàng từ phía trên - bạn có thể chỉ cần để microservice bán hàng gửi tin nhắn đến hệ thống đặt hàng, được cam kết trong cùng một giao dịch ghi việc bán hàng vào cơ sở dữ liệu." Không chắc chắn nếu những gì bạn đang đề xuất về cơ bản là một giao dịch phân tán, có nghĩa là, bạn sẽ xử lý kịch bản rollback như thế nào trong trường hợp này? Ví dụ, tin nhắn được cam kết xếp hàng tin nhắn nhưng đã được khôi phục về phía DB.
sactiw

$ . "Thực tế" hay còn gọi là "lệnh" sau đó được xử lý không đồng bộ sau khi giao dịch được thực hiện, sử dụng cơ chế thử lại tự động. Xem bài viết blog từ 2018-03-10 để biết ví dụ. Tránh khôi phục hoặc bồi thường có lợi cho chiến lược chuyển tiếp nếu có thể vì nó dễ thực hiện hơn.
Ant Kutschera

1

Trong microservice có ba cách để đạt được sự thống nhất giữa diff. dịch vụ:

  1. Phối hợp - Một quy trình quản lý giao dịch và phục hồi trên các dịch vụ.

  2. Vũ đạo - Dịch vụ chuyển tin nhắn giữa nhau và cuối cùng đạt đến trạng thái nhất quán.

  3. Lai - Trộn hai thứ trên.

Để đọc đầy đủ, hãy truy cập liên kết: https://medium.com/capital-one-developers/microservice-when-to-react-vs-orchestrate-c6b18308a14c


1

Có nhiều giải pháp thỏa hiệp hơn tôi thấy thoải mái. Cấp, nếu trường hợp sử dụng của bạn phức tạp, chẳng hạn như chuyển tiền giữa các ngân hàng khác nhau, các lựa chọn thay thế dễ chịu hơn có thể là không thể. Nhưng hãy xem những gì chúng ta có thể làm trong kịch bản chung, nơi việc sử dụng microservice can thiệp vào các giao dịch cơ sở dữ liệu của chúng ta.

Tùy chọn 1: Tránh sự cần thiết cho các giao dịch nếu tất cả có thể

Rõ ràng và được đề cập trước đó, nhưng lý tưởng nếu chúng ta có thể quản lý nó. Các thành phần có thực sự thuộc về cùng một dịch vụ không? Hoặc chúng ta có thể thiết kế lại (các) hệ thống sao cho giao dịch trở nên không cần thiết? Có lẽ chấp nhận phi giao dịch là sự hy sinh hợp lý nhất.

Tùy chọn 2: Sử dụng hàng đợi

Nếu có đủ chắc chắn rằng dịch vụ kia sẽ thành công ở bất cứ điều gì chúng tôi muốn nó làm, chúng tôi có thể gọi nó thông qua một số hình thức xếp hàng. Mục được xếp hàng sẽ không được chọn cho đến sau này, nhưng chúng tôi có thể đảm bảo rằng mục đó được xếp hàng .

Ví dụ: giả sử rằng chúng tôi muốn chèn một thực thể và gửi e-mail, dưới dạng một giao dịch. Thay vì gọi máy chủ thư, chúng tôi xếp hàng e-mail trong một bảng.

Begin transaction
Insert entity
Insert e-mail
Commit transaction

Một nhược điểm rõ ràng là nhiều dịch vụ siêu nhỏ sẽ cần truy cập vào cùng một bảng.

Tùy chọn 3: Thực hiện công việc bên ngoài lần cuối, ngay trước khi hoàn thành giao dịch

Cách tiếp cận này dựa trên giả định rằng việc thực hiện giao dịch rất khó xảy ra.

Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction

Nếu các truy vấn không thành công, cuộc gọi bên ngoài chưa được thực hiện. Nếu cuộc gọi bên ngoài thất bại, giao dịch không bao giờ được cam kết.

Cách tiếp cận này đi kèm với những hạn chế mà chúng tôi chỉ có thể thực hiện một cuộc gọi bên ngoài và nó phải được thực hiện sau cùng (nghĩa là chúng tôi không thể sử dụng kết quả của nó trong các truy vấn của mình).

Tùy chọn 4: Tạo mọi thứ trong trạng thái chờ xử lý

Như được đăng ở đây , chúng ta có thể có nhiều microservice tạo các thành phần khác nhau, mỗi thành phần ở trạng thái chờ xử lý, không giao dịch.

Bất kỳ xác nhận được thực hiện, nhưng không có gì được tạo ra trong trạng thái dứt khoát. Sau khi mọi thứ đã được tạo thành công, mỗi thành phần được kích hoạt. Thông thường, thao tác này rất đơn giản và tỷ lệ xảy ra sự cố rất nhỏ, thậm chí chúng tôi có thể thích thực hiện kích hoạt không giao dịch.

Hạn chế lớn nhất có lẽ là chúng ta phải tính đến sự tồn tại của các mặt hàng đang chờ xử lý. Bất kỳ truy vấn chọn cần xem xét liệu có bao gồm dữ liệu đang chờ xử lý hay không. Hầu hết nên bỏ qua nó. Và cập nhật là một câu chuyện khác hoàn toàn.

Tùy chọn 5: Hãy để microservice chia sẻ truy vấn của nó

Không có lựa chọn nào khác làm điều đó cho bạn? Sau đó, hãy nhận được không chính thống .

Tùy thuộc vào công ty, điều này có thể không được chấp nhận. Tôi biết. Điều này là không chính thống. Nếu nó không được chấp nhận, hãy đi một con đường khác. Nhưng nếu điều này phù hợp với tình huống của bạn, nó sẽ giải quyết vấn đề một cách đơn giản và mạnh mẽ. Nó có thể chỉ là sự thỏa hiệp dễ chấp nhận nhất.

Có một cách để biến các truy vấn từ nhiều dịch vụ siêu nhỏ thành một giao dịch cơ sở dữ liệu đơn giản.

Trả về truy vấn, thay vì thực hiện nó.

Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction

Mạng khôn ngoan, mỗi microservice cần có thể truy cập từng cơ sở dữ liệu. Hãy ghi nhớ điều này, cũng liên quan đến việc nhân rộng trong tương lai.

Nếu các cơ sở dữ liệu liên quan đến giao dịch nằm trên cùng một máy chủ, đây sẽ là một giao dịch thông thường. Nếu chúng ở trên các máy chủ khác nhau, nó sẽ là một giao dịch phân tán. Mã là như nhau bất kể.

Chúng tôi nhận được truy vấn, bao gồm loại kết nối, tham số và chuỗi kết nối của nó. Chúng ta có thể gói nó trong một lớp Lệnh thực thi gọn gàng, giữ cho luồng có thể đọc được: Cuộc gọi microservice kết quả trong một Lệnh, mà chúng ta thực thi, như một phần của giao dịch của chúng ta.

Chuỗi kết nối là thứ mà microservice khởi tạo mang lại cho chúng ta, vì vậy đối với tất cả ý định và mục đích, truy vấn vẫn được coi là được thực hiện bởi microservice đó. Chúng tôi chỉ định tuyến vật lý thông qua microservice khách hàng. Điều đó làm cho một sự khác biệt? Chà, nó cho phép chúng ta đặt nó trong cùng một giao dịch với một truy vấn khác.

Nếu sự thỏa hiệp có thể chấp nhận được, cách tiếp cận này mang lại cho chúng ta tính giao dịch đơn giản của một ứng dụng nguyên khối, trong một kiến ​​trúc microservice.


0

Tôi sẽ bắt đầu với việc phân tách không gian vấn đề - xác định ranh giới dịch vụ của bạn . Khi được thực hiện đúng cách, bạn sẽ không bao giờ cần phải có giao dịch trên các dịch vụ.

Các dịch vụ khác nhau có dữ liệu, hành vi, lực lượng động lực, chính phủ, quy tắc kinh doanh riêng, v.v ... Khởi đầu tốt là liệt kê những khả năng cấp cao mà doanh nghiệp của bạn có. Ví dụ: tiếp thị, bán hàng, kế toán, hỗ trợ. Một điểm khởi đầu khác là cấu trúc tổ chức, nhưng lưu ý rằng có một sự cảnh báo - vì một số lý do (ví dụ về chính trị), nó có thể không phải là kế hoạch phân rã kinh doanh tối ưu. Cách tiếp cận chặt chẽ hơn là phân tích chuỗi giá trị . Hãy nhớ rằng, dịch vụ của bạn cũng có thể bao gồm mọi người, đó không phải là phần mềm. Các dịch vụ nên liên lạc với nhau thông qua các sự kiện .

Bước tiếp theo là khắc các dịch vụ này. Kết quả là bạn vẫn nhận được tổng hợp tương đối độc lập . Họ đại diện cho một đơn vị nhất quán. Nói cách khác, nội bộ của họ phải nhất quán và ACID. Các uẩn giao tiếp với nhau thông qua các sự kiện.

Nếu bạn nghĩ rằng tên miền của bạn đòi hỏi sự nhất quán trước tiên, hãy nghĩ lại. Không có hệ thống lớn và quan trọng nào được xây dựng với ý tưởng này. Tất cả đều được phân phối và cuối cùng phù hợp. Kiểm tra giấy cổ điển của Pat Helland .

Dưới đây là một số lời khuyên thiết thực về cách xây dựng một hệ thống phân tán.

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.