0

Error Handling & Dead Letter Queue (DLQ) | Kafka

Trong bất kỳ hệ thống xử lý message nào, việc xử lý lỗi đóng vai trò quan trọng trong việc đảm bảo độ tin cậy và khả năng phục hồi của hệ thống.

Khi xử lý message, lỗi có thể xảy ra vì nhiều lý do khác nhau, như lỗi tạm thời (vấn đề mạng, dịch vụ không khả dụng) hoặc lỗi vĩnh viễn (dữ liệu không hợp lệ, business exception). Bài viết này sẽ đề cập đến các chiến lược xử lý lỗi, tập trung vào việc sử dụng cơ chế retryDead Letter Queue (DLQ) trong kafka để quản lý message lỗi một cách hiệu quả.

Vấn đề I: Xử lý lỗi khi xử lý message

Khi hệ thống xử lý message gặp phải lỗi, ta cần quyết định cách xử lý message bị lỗi đó. Có hai loại lỗi chính:

  • lỗi có thể retry: Đây là những lỗi có thể chỉ xảy ra tạm thời, như lỗi timeout mạng hoặc dịch vụ không khả dụng. Trong trường hợp này, việc retry (thử lại) message có thể giúp xử lý thành công ở lần thử sau.
  • lỗi không thể retry: Đây là những lỗi phát sinh do dữ liệu không hợp lệ hoặc vi phạm business logic, mà dù retry bao nhiêu lần cũng không thể xử lý thành công.

=> Giải pháp: simple default retry image.png

Đối với Spring kafka khi xử lý message gặp exception (trừBadListenerFaildException) consumer sẽ retry lại 9 lần và khoảng cách giữa các lần là khoảng ~ 500ms. Trong quá trình đợi sẽ không xử lý các message khác. sau 10 attemps, consumer sẽ xử lý message tiếp theo( commit message lỗi). Dù chiến lược này khá đơn giản nhưng nó có một số hạn chế sẽ được đề cập trong phần tiếp theo.

Vấn đề II: simple default retry – Consumer Blocking & Khả năng xử lý

Simple default retry có một vài hạn chế:

  • Consumer Blocking: Trong thời gian chờ giữa các lần retry, consumer sẽ bị chặn, điều này làm giảm throughput và tăng độ trễ của message. Điều này có thể trở thành nút cổ chai trong các hệ thống có lưu lượng xử lý cao.
  • Servability: Simple default retry không linh hoạt. Không có khả năng kiểm soát đối với các loại lỗi khác nhau, chẳng hạn như liệu một số lỗi có nên retry hay xử lý ngay lập tức dưới dạng lỗi.
  • ́́Not scalable: Khi lượng message tăng lên, cách tiếp cận chặn này không mở rộng tốt, đặc biệt đối với các hệ thống yêu cầu high availability.

=>Giải pháp: Non-Blocking Retry

Để khắc phục các hạn chế này, ta cần một cơ chế retry linh hoạt hơn và non-blocking consumer. Có hai cách ý tưởng để thực hiện việc này:

  • lưu trên mem của consumer: Message có thể được lưu tạm thời trên bộ nhớ trong khi chờ retry. Tuy nhiên, cách tiếp cận này có một nhược điểm lớn: không đáng tin cậy. Nếu hệ thống gặp sự cố hoặc khởi động lại, các message này sẽ bị mất.
  • Lưu trong DB: Cách tiếp cận đáng tin cậy hơn là lưu các lần thử lại trong cơ sở dữ liệu. Điều này đảm bảo rằng retry sẽ không bị mất, ngay cả khi hệ thống gặp sự cố. Tuy nhiên, nó cũng phức tạp hơn vì việc quản lý retry và các hoạt động cơ sở dữ liệu có thể dẫn đến overhead.
  • Separate Retry Queue: Thay vì chặn consumer trong quá trình retry, ta có thể chuyển các lần retry sang các queue chuyên dụng. Khi một message thất bại, nó sẽ được gửi đến queue retry với một khoảng thời gian trễ. Sau khi hết thời gian trễ, message sẽ được xử lý lại từ queue retry. Điều này giúp consumer chính có thể tiếp tục xử lý các message khác.

image.png

==> giải pháp Separate Retry Queue tối ưu hơn hai giải pháp trên. Ta có thể có nhiều queue retry (ví dụ: Retry Queue 1, Retry Queue 2, v.v.) với thời gian trễ tăng dần giữa các lần thử. Ví dụ, lần retry đầu tiên có thể sau 5 giây, lần thứ hai sau 30 giây, và cứ tiếp tục.Cách tiếp cận này đảm bảo rằng các lần retry không chặn luồng xử lý chính, giúp hệ thống hoạt động hiệu quả hơn và mở rộng tốt hơn.

Vấn đề III: Fail again – Khi Retry không thành công

image.png

Ngay cả khi sử dụng queue retry, có những trường hợp message vẫn thất bại sau nhiều lần thử lại. Trong những trường hợp này, ta cần có chiến lược dự phòng cuối cùng đó là Dead Letter Queue (DLQ)

image.png

Khi một message thất bại sau nhiều lần retry, nó nên được gửi vào Dead Letter Queue (DLQ). DLQ là một queue đặc biệt, nơi các message thất bại được lưu trữ để kiểm tra sau. Ý tưởng ở đây là các message trong DLQ đại diện cho các lỗi cần can thiệp hoặc kiểm tra thủ công.

Các quy tắc tốt khi sử dụng DLQ:

  • Thiết lập giới hạn retry: Nên cấu hình chính sách retry với số lần retry tối đa trước khi message được gửi vào DLQ. Một gợi ý phổ biến là bắt đầu với 3 topic retry và sau đó chuyển message vào DLQ nếu nó vẫn tiếp tục thất bại.
  • Tránh retry vô hạn: Retry vô hạn có thể gây ra tình trạng quá tải và tăng tải lên hệ thống. Thay vào đó, ta nên dừng sớm và chuyển message vào DLQ sau một số lần retry hợp lý.
  • Alert/Tracking: Khi một message được gửi vào DLQ, team chịu trách nhiệm nên được thông báo để kiểm tra và xử lý. Điều này đảm bảo rằng không có message lỗi nào bị bỏ qua.

Tóm lại, việc xử lý lỗi trong các hệ thống xử lý message là một nhiệm vụ phức tạp. Kết hợp giữa cơ chế retry và Dead Letter Queue (DLQ) cung cấp một chiến lược mạnh mẽ để xử lý các lỗi tạm thời và vĩnh viễn. Trong khi cơ chế retry giúp xử lý các sự cố tạm thời, DLQ đảm bảo rằng các lỗi vĩnh viễn được ghi lại và xử lý, ngăn chặn việc mất mát message và đảm bảo tính tin cậy của hệ thống.

Tài liệu tham khảo:


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí