1. Tổng quan
Ngay cả khi chúng ta cấu hình các broker ở mức độ đáng tin cậy cao nhất có thể, toàn bộ hệ thống vẫn có thể mất dữ liệu nếu chúng ta không cấu hình các producer để đảm bảo độ tin cậy.
Dưới đây là hai ví dụ minh họa cho điều này:
- Chúng ta đã cấu hình các broker với ba bản sao (replicas) và vô hiệu hóa việc chọn leader không đồng bộ, vì vậy sẽ không bao giờ mất một message nào đã được cam kết (commit) vào cụm Kafka. Tuy nhiên, chúng ta lại cấu hình producer để gửi message với giá trị acks=1. Khi producer gửi một message, message này đã được ghi vào leader nhưng chưa được ghi vào các bản sao đồng bộ. Leader gửi lại một phản hồi cho producer nói rằng "Message đã được ghi thành công" và sau đó bị lỗi ngay lập tức trước khi dữ liệu được sao chép sang các bản sao khác. Các bản sao khác vẫn được coi là đồng bộ (nhớ rằng sẽ mất một khoảng thời gian trước khi một bản sao được coi là không đồng bộ), và một trong số chúng sẽ trở thành leader. Do message chưa được ghi vào các bản sao khác, nó đã bị mất. Nhưng ứng dụng producer lại nghĩ rằng thông điệp đã được ghi thành công. Hệ thống vẫn đảm bảo tính nhất quán vì không có consumer nào thấy được message (nó chưa bao giờ được cam kết do các bản sao chưa nhận được), nhưng từ góc nhìn của producer, một message đã bị mất.
- Chúng ta đã cấu hình các broker với ba bản sao (replicas) và vô hiệu hóa việc chọn leader không đồng bộ. Sau khi rút kinh nghiệm từ những sai sót trước, chúng ta bắt đầu produce message với giá trị acks=all. Giả sử chúng ta đang cố gắng ghi một message vào Kafka, nhưng leader của partition mà chúng ta đang ghi vào vừa gặp sự cố và một leader mới đang được bầu chọn. Lúc này, Kafka sẽ phản hồi với thông báo "Leader không khả dụng." Nếu producer không xử lý lỗi đúng cách và không thử lại cho đến khi việc ghi thành công, message có thể bị mất. Một lần nữa, đây không phải là vấn đề về độ tin cậy của broker vì broker chưa bao giờ nhận được message cũng không phải là vấn đề về tính nhất quán vì các consumer chưa bao giờ nhận được message đó. Tuy nhiên, nếu producer không xử lý lỗi đúng cách, chúng có thể dẫn đến việc mất message
Như các ví dụ đã chỉ ra, có hai điều quan trọng mà bất kỳ ai viết ứng dụng produce dữ liệu lên Kafka cần chú ý:
- Sử dụng cấu hình acks phù hợp để đáp ứng các yêu cầu về độ tin cậy
- Xử lý lỗi đúng cách cả trong cấu hình lẫn mã nguồn (code)
2. Send Acknowledgments
acks=0
Điều này có nghĩa là một message được coi là ghi thành công vào Kafka nếu producer đã gửi được nó qua mạng. Chúng ta vẫn sẽ nhận được lỗi nếu đối tượng đang gửi không thể được tuần tự hóa hoặc nếu card mạng bị hỏng, nhưng sẽ không nhận được lỗi nếu partition đang offline, đang có quá trình bầu chọn leader, hoặc thậm chí nếu toàn bộ cụm Kafka không khả dụng. Chạy với acks=0 có độ trễ gửi thấp, nhưng nó sẽ không cải thiện độ trễ đầu cuối (nhớ rằng consumer sẽ không thấy message cho đến khi chúng được sao chép tới tất cả các bản sao sẵn có).
acks=1
Điều này có nghĩa là leader sẽ gửi một thông báo xác nhận (ack) hoặc lỗi ngay khi nó nhận được message và ghi vào partition data file (nhưng không nhất thiết phải đồng bộ lên đĩa). Chúng ta có thể mất dữ liệu nếu leader tắt hoặc bị lỗi, và một số message đã được ghi thành công vào leader và đã được xác nhận nhưng chưa được sao chép tới các follower trước khi sự cố xảy ra. Với cấu hình này, cũng có khả năng ghi dữ liệu vào leader nhanh hơn tốc độ mà nó có thể sao chép các message, dẫn đến các partition được sao chép không đủ, vì leader sẽ xác nhận message từ producer trước khi sao chép chúng.
acks=all
Điều này có nghĩa là leader sẽ đợi cho đến khi tất cả các bản sao đồng bộ nhận được message trước khi gửi lại thông báo xác nhận hoặc lỗi. Kết hợp với cấu hình min.insync.replicas trên broker, điều này cho phép chúng ta kiểm soát số lượng bản sao nhận được message trước khi nó được xác nhận. Đây là tùy chọn an toàn nhất—producer sẽ không dừng việc cố gắng gửi message cho đến khi nó được cam kết hoàn toàn. Tuy nhiên, đây cũng là tùy chọn có độ trễ cao nhất đối với producer—producer phải đợi cho đến khi tất cả các bản sao đồng bộ nhận được tất cả các message trước khi có thể đánh dấu batch message là "hoàn thành" và tiếp tục.
3. Configuring Producer Retries
Xử lý lỗi trong producer có hai phần: lỗi mà producer xử lý tự động và lỗi mà chúng ta, các nhà phát triển sử dụng thư viện producer, cần phải xử lý.
Producer có thể xử lý các lỗi có thể thử lại (retriable errors). Khi producer gửi message đến broker, broker có thể trả về mã thành công hoặc mã lỗi. Các mã lỗi này thuộc hai loại: lỗi có thể giải quyết sau khi thử lại và lỗi không thể giải quyết. Ví dụ, nếu broker trả về mã lỗi LEADER_NOT_AVAILABLE, producer có thể thử gửi message lại—có thể một broker mới đã được bầu chọn và lần thử thứ hai sẽ thành công. Điều này có nghĩa là LEADER_NOT_AVAILABLE là lỗi có thể thử lại. Ngược lại, nếu broker trả về ngoại lệ INVALID_CONFIG, việc thử lại message trong khi không thay đổi cấu hình. Đây là một ví dụ về lỗi không thể thử lại.
Nói chung, khi mục tiêu của chúng ta đề ra là không bao giờ mất message, cách tiếp cận tốt nhất là cấu hình producer để tiếp tục gửi lại message khi gặp lỗi. Nên để số lần thử lại ở mức mặc định hiện tại (MAX_INT, hoặc hiệu quả là vô hạn) và sử dụng delivery.timeout.ms
để cấu hình thời gian tối đa mà chúng ta sẵn sàng chờ đợi trước khi từ bỏ việc gửi một message—producer sẽ thử gửi message nhiều lần nhất có thể trong khoảng thời gian này.
Việc gửi lại message bị lỗi bao gồm rủi ro là cả hai message đều được ghi thành công vào broker, dẫn đến việc bị trùng lặp. Việc thử lại và xử lý lỗi cẩn thận có thể đảm bảo rằng mỗi message sẽ được lưu trữ ít nhất một lần, nhưng không đảm bảo chỉ một lần. Sử dụng enable.idempotence=true
sẽ khiến producer thêm thông tin bổ sung vào các bản ghi của mình, mà các broker sẽ sử dụng để bỏ qua các message trùng lặp do các lần thử lại gây ra.
4. Additional Error Handling
Việc sử dụng các lần thử lại (retry) tích hợp sẵn của producer là cách dễ dàng để xử lý đúng cách nhiều loại lỗi mà không làm mất message. Tuy nhiên, với tư cách là các nhà phát triển, chúng ta vẫn cần phải có khả năng xử lý các loại lỗi khác, bao gồm:
- Các lỗi broker không thể thử lại, chẳng hạn như lỗi liên quan đến kích thước message, authorization errors v.v.
- Các lỗi xảy ra trước khi message được gửi đến broker—ví dụ, serialization errors
- Các lỗi xảy ra khi producer đã sử dụng hết tất cả các lần thử lại hoặc khi bộ nhớ có sẵn của producer đã được lấp đầy do việc sử dụng toàn bộ để lưu trữ message trong khi thử lại
- Thời gian chờ (Timeouts)
5. Thông tin kết nối
Nếu anh em muốn trao đổi thêm về bài viết, hãy kết nối với mình qua LinkedIn và Facebook:
- LinkedIn: https://www.linkedin.com/in/nguyentrungnam/
- Facebook: https://www.facebook.com/trungnam.nguyen.395/
Rất mong được kết nối và cùng thảo luận!