6

Tôi đang cố triển khai kiến ​​trúc hướng sự kiện để xử lý các giao dịch phân tán. Mỗi dịch vụ có cơ sở dữ liệu riêng và sử dụng Kafka để gửi tin nhắn để thông báo cho các dịch vụ nhỏ khác về các hoạt động.Cách triển khai kiến ​​trúc theo hướng sự kiện microservice với Spring Cloud Stream Kafka và Cơ sở dữ liệu trên mỗi dịch vụ

Một ví dụ:

Order service -------> | Kafka |------->Payment Service 
     |          | 
Orders MariaDB DB     Payment MariaDB Database 

theo thứ tự nhận được một yêu cầu đặt hàng. Nó phải lưu trữ thứ tự mới trong DB của nó và xuất bản một tin nhắn để dịch vụ thanh toán nhận ra nó có tính phí cho các mục:

riêng Đặt hàngBusiness orderBusiness;

@PostMapping 
public Order createOrder(@RequestBody Order order){ 
    logger.debug("createOrder()"); 
    //a.- Save the order in the DB 
    orderBusiness.createOrder(order); 
    //b. Publish in the topic so that Payment Service charges for the item. 
    try{ 
     orderSource.output().send(MessageBuilder.withPayload(order).build()); 
    }catch(Exception e){ 
     logger.error("{}", e); 
    } 
    return order; 
} 

Đây là những nghi ngờ của tôi:

  1. bước A. (lưu trong thứ tự DB) và b.- (công bố thông điệp) nên được thực hiện trong một giao dịch, nguyên tử. Làm thế nào tôi có thể đạt được điều đó?
  2. Điều này liên quan đến điều trước: Tôi gửi tin nhắn với: orderSource.output() gửi (MessageBuilder.withPayload (order) .build()); Các hoạt động này là không đồng bộ và ALWAYS trả về true, bất kể nhà môi giới Kafka có hoạt động không. Làm thế nào tôi có thể biết rằng thông điệp đã đến được nhà môi giới Kafka?

Trả lời

8

bước A. (lưu trong thứ tự DB) và b.- (công bố thông điệp) nên được thực hiện trong một giao dịch, nguyên tử. Làm thế nào tôi có thể đạt được điều đó?

Kafka hiện không hỗ trợ giao dịch (và do đó cũng không có rollback hoặc cam kết), bạn cần phải đồng bộ hóa một cái gì đó như thế này. Vì vậy, trong ngắn hạn: bạn không thể làm những gì bạn muốn làm. Điều này sẽ thay đổi trong tương lai gần, khi KIP-98 được hợp nhất, nhưng có thể mất chút thời gian. Ngoài ra, ngay cả với các giao dịch trong Kafka, một giao dịch nguyên tử trên hai hệ thống là một việc rất khó làm, mọi thứ tiếp theo sẽ chỉ được cải thiện nhờ hỗ trợ giao dịch trong Kafka, nó vẫn sẽ không giải quyết được hoàn toàn vấn đề của bạn. Để làm được điều đó, bạn cần xem xét việc triển khai một số hình thức two phase commit trên các hệ thống của mình.

Bạn có thể nhận được phần nào gần bằng cách cấu hình các thuộc tính sản xuất, nhưng cuối cùng bạn sẽ phải lựa chọn giữa ít nhất một lần hoặc tối đa một lần cho một trong những hệ thống của bạn (MariaDB hay Kafka).

Hãy bắt đầu với những gì bạn có thể làm trong Kafka để đảm bảo việc gửi tin nhắn và tiếp tục đi sâu vào các lựa chọn của bạn cho quy trình tổng thể và hậu quả là gì.

giao hàng được đảm bảo

Bạn có thể cấu hình bao nhiêu người môi giới phải xác nhận nhận được tin nhắn của bạn, trước khi yêu cầu được trả lại cho bạn những thông số ack: bằng cách thiết lập này để tất cả bạn nói sự môi giới chờ đợi cho đến khi tất cả các bản sao đã nhận được tin nhắn của bạn trước khi trả lời câu trả lời cho bạn. Điều này vẫn không đảm bảo 100% rằng thư của bạn sẽ không bị mất, vì nó chỉ được ghi vào bộ nhớ cache của trang và có các kịch bản lý thuyết với một nhà môi giới thất bại trước khi nó được lưu vào đĩa, nơi thư vẫn có thể bị mất. Nhưng đây là một sự bảo đảm tốt như bạn sẽ nhận được. Bạn có thể tiếp tục giảm nguy cơ mất dữ liệu bằng cách giảm intervall mà tại đó các nhà môi giới buộc một fsync vào đĩa (văn bản được nhấn mạnh và/hoặc flush.ms) nhưng xin lưu ý rằng các giá trị này có thể mang lại hiệu suất cao hình phạt.

Ngoài các cài đặt này, bạn sẽ cần phải đợi nhà sản xuất Kafka trả lại phản hồi cho yêu cầu của bạn cho bạn và kiểm tra xem có ngoại lệ hay không. Loại quan hệ này vào phần thứ hai của câu hỏi của bạn, vì vậy tôi sẽ đi sâu hơn nữa. Nếu đáp ứng được sạch sẽ, bạn có thể chắc chắn rằng dữ liệu của bạn có thể đến Kafka và bắt đầu lo lắng về MariaDB.

Mọi thứ chúng tôi đã đề cập đến chỉ nhằm đảm bảo rằng Kafka nhận được thư của bạn, nhưng bạn cũng cần ghi dữ liệu vào MariaDB và điều này cũng có thể bị lỗi. gửi đến Kafka - và điều này bạn không thể làm được.

Vì vậy, về cơ bản bạn cần phải chọn một hệ thống trong đó bạn có thể tốt hơn để đối phó với các bản sao/giá trị còn thiếu (tuỳ thuộc vào việc hay không bạn gửi lại thất bại một phần) và điều đó sẽ ảnh hưởng đến thứ tự bạn làm những điều trong.

Lựa chọn 1

Kafka first

trong tùy chọn này, bạn khởi tạo một giao dịch trong MariaDB, sau đó gửi thông điệp tới Kafka, chờ đợi một câu trả lời và nếu gửi thành công bạn cam kết giao dịch trong MariaDB. Nên gửi đến Kafka không thành công, bạn có thể khôi phục lại giao dịch của bạn trong MariaDB và mọi thứ đều dandy. Tuy nhiên, nếu gửi đến Kafka thành công và cam kết của bạn với MariaDB không thành công vì lý do nào đó, thì không có cách nào lấy lại thông điệp từ Kafka. Vì vậy, bạn sẽ hoặc là thiếu một tin nhắn trong MariaDB hoặc có một thông điệp trùng lặp trong Kafka, nếu bạn gửi lại tất cả mọi thứ sau này.

Lựa chọn 2

MariaDB first

này là khá nhiều chỉ là cách khác xung quanh, nhưng bạn có lẽ tốt hơn có thể xóa một tin nhắn được viết bằng MariaDB, tùy thuộc vào mô hình dữ liệu của bạn.

Tất nhiên bạn có thể giảm thiểu cả hai cách tiếp cận bằng cách theo dõi các lần gửi không thành công và chỉ thử lại sau này, nhưng tất cả điều đó là chi tiết của một băng rộng về vấn đề lớn hơn.

Cá nhân tôi muốn đi với phương pháp 1, vì cơ hội cam kết không được nhỏ hơn một chút so với gửi chính nó và thực hiện một số loại kiểm tra dupe ở phía bên kia của Kafka.


này có liên quan đến trước một: Tôi gửi tin nhắn với:. orderSource.output() gửi (MessageBuilder.withPayload (theo thứ tự) .build()); Thao tác này không đồng bộ và ALWAYS trả về true, không quan trọng nếu nhà môi giới Kafka ngừng hoạt động. Làm thế nào tôi có thể biết rằng thông báo đã đạt đến nhà môi giới Kafka?

Trước hết, tôi thừa nhận rằng tôi không quen với Spring, vì vậy điều này có thể không được sử dụng cho bạn, nhưng đoạn mã sau minh họa một cách để kiểm tra phản hồi sản phẩm cho ngoại lệ. Bằng cách gọi tuôn ra, bạn chặn cho đến khi tất cả các lần gửi đã hoàn tất (và không thành công hoặc đã thành công) và sau đó kiểm tra kết quả.

Producer<String, String> producer = new KafkaProducer<>(myConfig); 
final ArrayList<Exception> exceptionList = new ArrayList<>(); 

for(MessageType message : messages){ 
    producer.send(new ProducerRecord<String, String>("myTopic", message.getKey(), message.getValue()), new Callback() { 
    @Override 
    public void onCompletion(RecordMetadata metadata, Exception exception) { 
     if (exception != null) { 
     exceptionList.add(exception); 
     } 
    } 
    }); 
} 

producer.flush(); 

if (!exceptionList.isEmpty()) { 
    // do stuff 
} 
+0

Tôi chỉnh sửa các câu hỏi với một cách tiếp cận mới Tôi sau, Tuy nhiên cho rằng bạn trả lời là rất rõ tôi đang quay trở lại nó với bản gốc và bắt đầu một cái mới với phiên bản đã chỉnh sửa. Tôi sẽ quay lại với phản hồi về câu trả lời của bạn. Cảm ơn! – codependent

+0

Sönke, mọi thứ rõ ràng, tôi đánh giá cao sự giải thích kỹ lưỡng. Đối với những người quan tâm đến cách đảm bảo việc gửi tin nhắn với Spring Cloud Stream: https://github.com/spring-cloud/spring-cloud-stream/issues/795 – codependent

+0

@codependent Làm thế nào về việc có cổng api hoặc bất cứ điều gì gửi để kafka đầu tiên và sau đó hai microservices đăng ký tin nhắn kafka .. không phải là điều này khả thi? hoặc bạn không làm điều này bởi vì bạn muốn có dữ liệu nhất quán thay vì cuối cùng nhất quán? –

2

Tôi nghĩ rằng cách thích hợp để thực hiện tổ chức sự kiện Sourcing là bởi có Kafka được điền trực tiếp từ sự kiện được đẩy bởi một plugin mà đọc từ RDBMS binlog ví dụ sử dụng confluent nước uống đóng chai (https://www.confluent.io/blog/bottled-water-real-time-integration-of-postgresql-and-kafka/) hoặc Debezium tích cực hơn (http://debezium.io/). Sau đó, tiêu thụ Microservices có thể lắng nghe những sự kiện đó, tiêu thụ chúng và hành động trên cơ sở dữ liệu tương ứng của chúng và cuối cùng là phù hợp với cơ sở dữ liệu RDBMS.

Có một cái nhìn vào đây để trả lời đầy đủ của tôi cho một phương châm: https://stackoverflow.com/a/43607887/986160

Các vấn đề liên quan