Một client viết sai có thể chiếm hết băng thông của cả cụm. Kafka có hạn ngạch để chặn chuyện đó. Bài này đo xem nó thật sự làm gì — và nó không làm gì.

Hạn ngạch hoạt động thế nào, và vì sao nó không chặn được bùng phát

Đặt hạn ngạch

kafka-configs.sh --bootstrap-server kf:9092 --alter \
  --entity-type clients --entity-name tham \
  --add-config 'producer_byte_rate=5242880'

5 MB mỗi giây cho client mang client.id=tham. Có hiệu lực ngay, không cần khởi động lại gì.

Nó hoạt động

300.000 tin × 500 byte = 150 MB, cùng broker:

Thông lượng
client tham (hạn ngạch 5 MB/giây) 4,38 MB/giây
client hiền (không hạn ngạch) 184,11 MB/giây

Chênh 42 lần, và con số 4,38 rất sát với hạn ngạch 5 MB đã đặt.

Nhưng nhìn độ trễ của client bị bóp:

trung bình 6.040 ms      lớn nhất 10.898 ms

Kafka điều tiết bằng cách làm chậm phản hồi, không bằng cách từ chối. Client bị điều tiết thấy độ trễ khổng lồ và không thấy lỗi nào.

Đây là chi tiết quan trọng nhất về hạn ngạch, vì nó quyết định ứng dụng của bạn phản ứng thế nào: không có ngoại lệ nào để bắt, không có mã lỗi nào để xử lý. Chỉ có send() trả về chậm hơn rất nhiều.

Hệ quả trực tiếp: delivery.timeout.ms phải lớn hơn thời gian bị điều tiết tệ nhất. Trong phép đo trên, độ trễ lớn nhất là 10,9 giây. Ứng dụng đặt thời gian chờ 5 giây sẽ báo lỗi gửi và có thể tự thử lại — trong khi Kafka đang hoạt động hoàn toàn bình thường và chỉ đang giữ đúng luật bạn đặt ra.

Vòng lặp tệ nhất: thời gian chờ hết → thử lại → gửi nhiều hơn → bị bóp mạnh hơn → thời gian chờ hết nhanh hơn.

Hạn ngạch không chặn được bùng phát

Đây là phát hiện lớn nhất của bài, và tôi tìm ra nó nhờ một phép đo hỏng.

Lần đo đầu, tôi gửi 100.000 tin và nhận 148,55 MB/giây — gấp ba mươi lần hạn ngạch. Tôi tưởng hạn ngạch chưa được áp dụng.

Nó đã được áp dụng. Vấn đề là đợt gửi quá ngắn.

Đo lại với nhiều cỡ đợt khác nhau, cùng client, cùng hạn ngạch 5 MB/giây:

Cỡ đợt Thông lượng đạt được
9 MB 53,88 MB/giây không bị điều tiết
23 MB 102,77 MB/giây không bị điều tiết
47 MB 150,90 MB/giây không bị điều tiết — gấp 30 lần hạn ngạch
95 MB 7,81 MB/giây bị điều tiết mạnh

Cửa sổ tính của Kafka là quota.window.size.seconds × quota.window.num, mặc định 1 × 11 = 11 giây. Broker tính tốc độ trung bình trượt trên cửa sổ đó, và chỉ điều tiết khi trung bình vượt hạn ngạch.

Nghĩa là một client bùng phát được tới khoảng 11 lần hạn ngạch trước khi bị bóp. 11 × 5 MB = 55 MB, và con số đó nằm đúng giữa 47 MB (qua được) và 95 MB (bị bóp).

Hạn ngạch bảo vệ broker khỏi tải kéo dài, không bảo vệ khỏi đột biến. Nếu mối lo của bạn là một job hàng loạt đẩy 40 MB trong hai giây và làm mọi thứ khác trễ, hạn ngạch không giải quyết được.

Hạ quota.window.num xuống làm cửa sổ ngắn lại và phản ứng nhanh hơn, nhưng cũng khiến việc điều tiết giật cục hơn với lưu lượng bình thường không đều.

Ba loại hạn ngạch

Giới hạn
producer_byte_rate Byte mỗi giây gửi vào
consumer_byte_rate Byte mỗi giây đọc ra
request_percentage Phần trăm thời gian luồng xử lý của broker

Cái thứ ba ít được dùng nhưng quan trọng: một client gửi rất nhiều yêu cầu nhỏ có thể chiếm hết luồng xử lý mà không vượt hạn ngạch byte nào. request_percentage=200 nghĩa là client được dùng tối đa hai luồng xử lý đầy đủ.

Nếu bạn thấy RequestHandlerAvgIdlePercent (phần 31) tụt xuống mà băng thông vẫn bình thường, đây là chỗ nên nhìn.

Ba mức áp dụng

# theo client-id
--entity-type clients --entity-name tham

# theo user (cần có xác thực)
--entity-type users --entity-name doi-phan-tich

# mặc định cho mọi client chưa khai riêng
--entity-type clients --entity-default

Mức mặc định là thứ đáng đặt trước tiên trên cụm dùng chung. Không có nó, một client mới triển khai không giới hạn có thể chiếm hết băng thông trước khi ai kịp phản ứng.

Kafka chọn hạn ngạch cụ thể nhất khớp với client. Thứ tự ưu tiên là (user, client-id) → user → client-id → mặc định.

Hạn ngạch chia theo broker, không chia theo cụm

Điểm dễ hiểu nhầm: producer_byte_rate=52428805 MB/giây trên mỗi broker, không phải trên cả cụm.

Với cụm 6 broker, một client ghi đều vào mọi partition có thể đạt tới 30 MB/giây tổng. Chia hạn ngạch bạn muốn cho số broker khi đặt con số.

client.id là thứ dễ giả

Trên cụm không xác thực, client.id chỉ là một chuỗi client tự khai. Ai cũng đặt được thành bất cứ gì, kể cả trùng với một client khác hoặc để trống.

Nghĩa là hạn ngạch theo client.id là một thoả thuận vận hành, không phải một cơ chế bảo vệ. Nó ngăn được lỗi vô ý, không ngăn được cố ý.

Muốn hạn ngạch có tính cưỡng chế thì phải áp theo user, và điều đó đòi có xác thực — phần sau.

Theo dõi việc điều tiết

Broker phát ra chỉ số cho biết client nào đang bị bóp và bao lâu:

kafka.server:type=Produce,client-id=tham        -> throttle-time
kafka.server:type=Fetch,client-id=tham          -> throttle-time

Đây là chỉ số nên có trên bảng theo dõi, vì phía client không thấy gì ngoài độ trễ. Khi ai đó báo "Kafka chậm", đây là chỗ đầu tiên nên nhìn — có thể nó không chậm, mà đang giữ đúng luật.

Thử ba mươi giây

Xem cụm của bạn có hạn ngạch nào không:

kafka-configs.sh --bootstrap-server kf:9092 --describe --entity-type clients
kafka-configs.sh --bootstrap-server kf:9092 --describe --entity-type users

Không có dòng nào nghĩa là mọi client đều không giới hạn — và một job hàng loạt viết sai có thể làm chậm mọi thứ khác. Đặt một mức mặc định rộng rãi là việc mất ba mươi giây và tránh được một loại sự cố.

Phần sau đo bảo mật: TLS làm việc đọc chậm đi 2,2 lần, và lý do nằm ở phần 19.