Exactly one message với Kafka (EOS)
Nói về exactly one message trong hệ thống phân tán thì cũng giống như nói về hố đen vậy. Bới vì lúc đầu nó chỉ là 1 ý niệm. Không ai biết nó có tồn tại hay không cho tới khi mấy sếp chụp được bức ảnh đầu tiên về hố đen năm 2019 (nhưng cũng chỉ là chấm mờ và tới 2021 mới có bức ảnh cho nó đàng hoàng).
Ngày nay đã có rất nhiều pattern cùng các công nghệ kèm theo để anh em dev đạt được mục tiêu deliver exactly one message trong hệ thống. Hôm nay tôi xin mạn phép ghi ra hiểu biết của mình về kafka và concept EOS (exactly-one semantic) với Kafka nhé.
Kafka có 2 mode khá phổ biến là at least one và at most one. Với mode at least one ta thường bị vấn đề về duplicate message, còn với mode at most one thì ta lại phải xử lý tình trạng loss message. Vậy nguyên nhân là do đâu và kafka cũng như anh em dev đã xử nó như thế nào ?
Nguyên nhân chính được define là do các mode ACK trên Kafka producer và cả cách mà consumer xử lý commit message.
Đầu tiên ta tới với thằng producer
Messgae được push từ producer tới Kafka thông qua Replication leader và chờ ack báo thành công từ Replication leader sau khi nó append xong message. Tuy nhiên không phải lúc nào mọi thứ cũng hoàn hảo như vậy. Khi producer chờ mãi mà vẫn không thấy ack về nó sẽ thực hiện các cơ chế về retry push message, điều này vô tình gây ra những phiền phức cho hệ thống.
ACK = 1 hoặc ACK = 0 → loss message
Khi đặt mode ACK = 1, replication leader sau khi nhận được message append message này tại node của nó rồi báo ack thành công tới producer mà không đợi message được sync thành công tới các note khác trong cluster.
Điều này dẫn tới nếu xảy ra lỗi tại bước sync message và sau đó node replication leader bị chết sẽ dẫn tới mất message.
Với ACK = 0 thậm chí còn tệ hơn. Producer cứ push mù mà chả quan tâm message đã được append xuống data kafka hay chưa.
ACK = ALL, chính nó là thằng làm cho Kafka bị dupplicate message.
Khi đặt ACK = ALL, Kafka producer sau khi gửi message tới replication leader, anh này sẽ chờ tới khi append thành công sync log ở các replication (nghĩa là message được replica tới tất cả các node trong cluster) thì mới báo ACK thành công tới producer.
Tuy nhiên khi send ACK về producer thì replication bị Quang tèo, producer chờ mãi ko thấy ACK nên retry push message. Lúc này Kafka chọn mặt gửi vàng cho 1 node khác làm replication leader. Node này hồn nhiên nhận message và sync log qua các node khác trong cluster gây ra duplicate message.
Thế Kafka đã xử 2 vấn đề trên như thế nào
Kafka buff bẩn cho producer và gọi nó là Idempotent producer (producer ???? mình không biết dịch từ này )). Cụ thể producer có thêm 2 vũ khí là
- Producer ID (PID):
- Mỗi Kafka producer khi khởi tạo sẽ được gán một Producer ID (PID) duy nhất. Producer ID này được sử dụng để phân biệt các producer khác nhau.
- Sequence Number (SN):
- Mỗi bản ghi mà producer gửi đi sẽ có một sequence number (SN) duy nhất, bắt đầu từ 0 cho mỗi partition mà producer đang ghi dữ liệu vào.
- Mỗi partition sẽ theo dõi sequence number của từng producer ID. Khi producer gửi một bản ghi mới, Kafka sẽ kiểm tra sequence number:
- Nếu SN khớp với số tiếp theo mà Kafka mong đợi, bản ghi sẽ được ghi vào.
- Nếu SN nhỏ hơn giá trị mong đợi (có nghĩa là bản ghi đã được ghi rồi), Kafka sẽ bỏ qua (ignore) bản ghi đó để tránh việc ghi đúp.
- Nếu SN lớn hơn, Kafka sẽ báo lỗi (producer có thể gửi lại đúng bản ghi).
Nhờ có Producer ID và Sequence Number, producer có thể retry gửi lại bản ghi mà không sợ tạo ra bản ghi trùng lặp trong Kafka. Kafka sẽ biết rằng bản ghi đó đã được xử lý rồi và bỏ qua nó nếu nó là bản ghi cũ từ đó tránh được việc duplicate message.
Điều may mắn là anh em dev không cần phải implement hay thậm chí là cấu hình cho kafka producer sử dụng Idempotent producer bởi đây là mode mặc định của kafka từ version 3.0. Ae có thể đọc thêm tại link này: https://cwiki.apache.org/confluence/display/KAFKA/KIP-679%3A+Producer+will+enable+the+strongest+delivery+guarantee+by+default
Tiếp đến là thằng consumer (chính chúng ta đây ( )
Đọc tới đây chắc anh em cũng mường tượng ra được 2 lỗi tương ứng với 2 cách xử lý của consumer rồi. Tương tự như producer, consumer cũng có 2 kiểu process tương ứng với 2 concept là at most one và at least one
AT MOST ONE
Với concept at most one, sau khi nhận được message từ Kafka, consumer cứ commit message này cái đã rồi tính (giống kiểu sếp cứ hứa tăng lương cái đã rồi có tăng hay không thì từ từ). Sau khi commit thì message được Kafka đánh dấu là đã được consumer xử lý, vì thế nên trong quá trình consumer xử lý xảy ra lỗi hay xui hơn thì kiểu consumer bị mất điện, restart,… thì message cũng bay màu theo luôn (giống nguyện vọng tăng lương cuả ae).
AT LEAST ONE
Concept at least one thì ngược lại. Khi nhận được message thì consumer xử lý cái đã, bao giờ xử lý xong thì commit sau. Điều này dẫn tới nhiều trường hợp message đã được consumer xử lý xong tuy nhiên khi commit message lên Kafka thì bị lỗi (mất mạng, ông dev nào rút nhầm dây server để cắm sạc điện thoại,…). Kafka không thấy phản hồi (hoặc phản hồi là lỗi) về việc xử lý message sẽ đánh giá consumer này hơi kém về mặt chuyên môn và chuyển message qua cho consumer khác xử lý. Consumer mới được giao task thì cứ thế làm thôi, làm message được xử lý tới 2 lần (duplicate message).
Vậy anh em dev xử lý 2 case trên thế nào
Thông thường ae sẽ xử lý theo concept 2 tức là nhận message → xử lý → xong thì mới commit. Và để tránh bị duplicate message mỗi message sẽ được gắn thêm 1 id (transaction id hay message id) duy nhất. Sau khi message được xử lý thì lưu lại thông tin đã xử lý với message có id đấy. Nếu kafka produce message có id đã được xử lý tới thì mình đơn giản là ignore nó đi thôi.
Ngoài ra Kafka cung cấp các cơ chế transactional. Nghĩa là bọc cả phần process message và phần commit vào chung 1 transaction. Cơ chế này được hỗ trợ nếu phần process message của anh em chỉ tương tác với kafka như stream data, push sang topic khác,… còn nếu có tương tác với 3nd party như database hay call api thì chịu. Tôi đã test với kafka streams exactly one và nó ngon lành nha anh em.
Vậy là hết bài viết rồi, chúc anh em build một hệ thống với kafka thật là exactly one nhé
All rights reserved