Building Scalable Real Time Event Processing with Kafka and Flink

Bài tóm tắt DoorDash 2022 3. Quy mô lớn Độ khó: Nâng cao

DoorDash · 2022 · 3. Quy mô lớn · Độ khó: Nâng cao — bài gốc

Bối cảnh

  • Event từ service và thiết bị phải tới warehouse OLAP (Snowflake), tới nền tảng ML (feature realtime như thời gian chờ trung bình của nhà hàng), tới backend metric (Chronosphere) để giám sát bản app mới.
  • Trước đây: vài pipeline từ monolith vào Snowflake, mỗi cái dựng một kiểu, chỉ xử lý một loại event, đi qua nhiều hệ queue khác nhau không có giám sát: latency cao, tốn tiền, khó vận hành.

Thay đổi chính

  • Iguazu (bắt đầu khoảng 2020): chuyển từ dựa vào dịch vụ AWS/bên thứ ba sang Kafka + Flink mã nguồn mở tuỳ biến được. Tới 2022: hàng trăm tỉ event/ngày, tỉ lệ giao 99,99%.
  • Ghi event qua Kafka Rest Proxy (của Confluent, tuỳ biến) thay vì nhúng Kafka client vào mọi service: không bắt team cấu hình Kafka, cấu hình producer thống nhất, mobile/web gửi được qua HTTP, gom lô qua nhiều client giảm CPU broker. Thêm: ghi tới nhiều cụm Kafka, ghi bất đồng bộ (trả lời ngay khi payload hợp lệ và vào buffer; chấp nhận mất ít khi Kafka sập, bù bằng retry và theo dõi ack), readiness probe nạp sẵn metadata và ghi thử để tránh cold start. Proxy chạy trên Kubernetes với HPA theo CPU.
  • Tinh chỉnh cho event không giao dịch: replication factor 2, min in-sync 1, ack khi leader ghi xong, sticky partitioner với linger 50–100 ms; CPU broker giảm 30–40%.
  • Tiêu thụ bằng Flink ở hai mức: DataStream API (clone template, cấu hình bằng Terraform, mỗi job một cụm standalone trên Kubernetes qua Helm để cô lập lỗi và scale riêng) và SQL khai báo trong YAML cho người phân tích (mở PR, merge xong CD biên dịch và deploy).
  • Định dạng event thống nhất: envelope (ngữ cảnh, metadata, tham chiếu schema, phần JSON không schema để đổi linh hoạt) + payload. Envelope nằm trong Kafka header. Event định nghĩa bằng Protobuf trong một repo chung; thư viện tự chuyển sang Avro cho đích cần. Payload từ service bị kiểm schema và loại ngay ở producer; JSON từ mobile/web được một job stream riêng kiểm và chuyển đổi.
  • Đăng ký schema lúc build chứ không lúc chạy: CI của repo Protobuf kiểm tương thích với schema registry, lỗi thì fail; merge xong CD mới đăng ký. Tránh dồn request lên registry và lỗi runtime khi schema không tương thích.
  • Vào warehouse hai bước: Flink StreamingFileSink ghi Parquet lên S3 (exactly-once theo checkpoint, phân thư mục để batch đọc nhanh, backfill được khi Kafka hết hạn giữ, mở đường cho lake với Trino) → Snowpipe nạp vào Snowflake khi có thông báo SQS. Mỗi event một job Flink và một Snowpipe để scale và cô lập lỗi.
  • Onboard một event cần nhiều PR vào nhiều repo Terraform; tự động bằng GitHub App tạo/merge PR theo quy trình duyệt, chạy như workflow Cadence, cộng UI Retool tìm schema và bấm onboard: từ vài ngày xuống vài phút.

Vì sao

  • Nhiều nguồn, nhiều đích, đội nhỏ: cần một nền tảng chung dễ tự phục vụ thay vì mỗi luồng một pipeline riêng.

Bài học cho SWE

  • Proxy ghi tập trung là chỗ tối ưu và đổi hành vi producer mà không phải phối hợp với mọi client.
  • Cấu hình Kafka mặc định thiên về nhất quán; với event analytics có thể đổi bớt độ bền lấy thông lượng, nhưng phải có chủ đích.
  • Kiểm tương thích schema ở CI biến lỗi runtime thành lỗi build.
  • Tách bước "ghi file lên object storage" khỏi "nạp vào warehouse" để sự cố warehouse không làm dừng stream.
  • Một pipeline mỗi event tăng cô lập nhưng tăng hạ tầng; phải tự động hoá onboard mới gánh được.

Liên quan