Bài 2

Ai đọc partition nào

Trong một consumer group, mỗi partition được giao cho đúng một consumer. Việc chia đó do một lớp assignor làm, và Kafka có bốn lớp sẵn cho ra bốn kết quả khác nhau trên cùng một cấu hình. Bài này đo cả bốn trên Kafka 4.3.1, trong đó có một trường hợp 9 partition chia cho 4 consumer mà vẫn có một consumer không nhận gì.

Luật duy nhất

Một partition được giao cho nhiều nhất một consumer trong cùng group. Từ đó ra hai hệ quả mà ai cũng biết: số consumer làm việc không bao giờ vượt quá số partition, và số partition là trần cứng của mức song song. Điều ít được nói tới là phần còn lại — cách chia — không phải một chi tiết nhỏ. Nó quyết định liệu trần đó có được dùng hết hay không.

Bộ đo dưới đây dựng group bằng chính công cụ đi kèm Kafka, rồi đọc kết quả gán ra:

kafka-console-consumer.sh --include 't3[abc]' --group g1 \
  --consumer-property group.protocol=classic \
  --consumer-property partition.assignment.strategy=\
org.apache.kafka.clients.consumer.RangeAssignor

kafka-consumer-groups.sh --describe --group g1 --members --verbose
# CLIENT-ID  #PARTITIONS  CURRENT-ASSIGNMENT
# c1         6            t3a:0,1;t3b:0,1;t3c:0,1
# c2         3            t3a:2;t3b:2;t3c:2

range chia theo từng topic

RangeAssignor là mặc định. Nó xử lý từng topic một cách độc lập: với mỗi topic, mỗi consumer nhận ⌊P/C⌋ partition liền nhau, và P mod C consumer đầu danh sách nhận thêm một partition.

Trên một topic đơn lẻ, cách chia đó hợp lý. Trên nhiều topic thì nó lặp lại y hệt, nên phần dư của mọi topic đều rơi vào cùng những consumer đầu danh sách. Ba topic cùng dư một partition nghĩa là consumer đầu nhận thêm ba, chứ phần dư không được rải ra.

Đo trên ba topic 3 partition, hai consumer:

consumerđược gánsố partition
c1t3a:0,1;t3b:0,1;t3c:0,16
c2t3a:2;t3b:2;t3c:23

Sáu so với ba: một consumer gánh gấp đôi. Cùng cấu hình đó, roundrobin cho 5 và 4.

Trường hợp đáng nhớ nhất: 9 partition, 4 consumer, một người ngồi không Vẫn ba topic 3 partition, giờ bốn consumer. Mỗi topic có 3 partition chia cho 4 consumer, nên ⌊3/4⌋ = 0 và ba consumer đầu mỗi người nhận một. Consumer thứ tư không nhận gì — ở mọi topic. Đo được: 3, 3, 3, 0. Với roundrobin trên đúng cấu hình đó: 3, 2, 2, 2.

roundrobin trải cả group

RoundRobinAssignor gộp mọi cặp topic–partition của cả group thành một danh sách, sắp theo tên topic rồi số hiệu partition, và phát lần lượt cho từng consumer. Vì danh sách không bị cắt theo topic nên phần dư của topic này bù vào topic kia.

Cùng ba topic 3 partition, hai consumer:

consumerđược gánsố partition
c1t3a:0,2;t3b:1;t3c:0,25
c2t3a:1;t3b:0,2;t3c:14

Chênh lệch tối đa giữa hai consumer bất kỳ luôn là 1 partition, vì đó là tính chất của việc phát vòng tròn. Trên mười consumer và 9 partition, roundrobin cho 9 consumer mỗi người một partition, một người trắng tay, trong khi range cho 3 consumer mỗi người ba partition và 7 người trắng tay. Cùng một group, cùng một tập topic.

Trên topic lệch nhau thì khác biệt cũng rõ: một topic 5 partition cộng một topic 1 partition, hai consumer. range cho 4 và 2; roundrobin cho 3 và 3.

sticky và cooperative-sticky

StickyAssignor cũng chia đều như roundrobin nhưng theo cách khác: nó xếp partition xen kẽ theo số hiệu — mọi partition số 0 của các topic trước, rồi mọi partition số 1 — rồi lấp đầy từng consumer tới hạn ngạch thay vì phát vòng tròn. Trên ba topic 3 partition, hai consumer, đo được:

consumerđược gánsố partition
c1t3a:0,1;t3b:0,1;t3c:05
c2t3a:2;t3b:2;t3c:1,24

Vẫn 5 và 4 như roundrobin, nhưng gom khác. Đúng bằng danh sách xen kẽ t3a:0, t3b:0, t3c:0, t3a:1, t3b:1, t3c:1, t3a:2, t3b:2, t3c:2 cắt ở phần tử thứ năm.

Quy tắc xen kẽ chỉ kiểm được tới 3 consumer Nó khớp từng partition ở 2 và 3 consumer, và ở 2 consumer trên cặp topic lệch 5 + 1. Nhưng với 4 consumer thì đo được bốn phần là t3a:0;t3b:0;t3c:0, t3a:1;t3b:1, t3a:2;t3b:2 và t3c:1,2: kích thước vẫn đúng 3, 2, 2, 2 như hạn ngạch dự đoán, nhưng cách gom thì khác. Không có thứ tự tĩnh nào giải thích được cả ba cỡ group, nên với sticky chỉ nên dựa vào kích thước phần chia. Phòng thí nghiệm bên dưới ghi rõ giới hạn này ngay dưới kết quả.

Cái tên “sticky” nói về những lần gán sau: khi một consumer rời group, nó cố giữ nguyên phần đã gán cho những consumer còn lại, thay vì xáo lại từ đầu. Phòng thí nghiệm trong bài này chỉ dựng lần gán đầu tiên, khi chưa có gì để bám lại.

cooperative-sticky chia y hệt sticky Chạy cả hai trên cùng cấu hình ba topic 3 partition, hai consumer: kết quả trùng từng partition. Khác biệt không nằm ở phép chia mà ở giao thức rebalance — cooperative chỉ thu hồi những partition thật sự phải chuyển, còn eager bắt cả group nhả hết rồi nhận lại. Với group lớn, đó là khác biệt giữa một khoảng ngưng ngắn và một khoảng ngưng dài, nhưng bảng gán cuối cùng thì như nhau.

Bảng số

Ba topic, mỗi topic 3 partition, tổng 9 partition. Số partition mỗi consumer nhận được:

số consumerrangeroundrobinstickyconsumer ngồi không
26, 35, 45, 4không ai
33, 3, 33, 3, 33, 3, 3không ai
43, 3, 3, 03, 2, 2, 23, 2, 2, 2range: 1 người
103, 3, 3, và 7 số 0chín số 1, một số 0chín số 1, một số 0range: 7 người

Topic lệch nhau — một topic 5 partition và một topic 1 partition, hai consumer:

chiến lượcc1c2
rangeu1:0;u5:0,1,2 — 4 partitionu5:3,4 — 2 partition
roundrobinu1:0;u5:1,3 — 3 partitionu5:0,2,4 — 3 partition
stickyu1:0;u5:0,1 — 3 partitionu5:2,3,4 — 3 partition

Với một topic duy nhất thì ba chiến lược gần như tương đương về số lượng, chỉ khác partition nào về tay ai: trên một topic 3 partition và hai consumer, range cho 0,1 và 2, còn roundrobin cho 0,2 và 1. Nói cách khác, mọi khác biệt trong bài này chỉ hiện ra khi group đọc từ nhiều hơn một topic. Group một topic thì chọn chiến lược nào cũng như nhau.

Thứ đoán được và thứ không

Engine trong phòng thí nghiệm tái hiện đúng tập các phần chia ở cả 16 kịch bản đã đo. Nhưng có một thứ nó không tái hiện được, và cũng không nên hứa: consumer nào nhận phần nào.

Cho đúng mười consumer tên c1…c10 vào một group range đọc một topic 3 partition, hai lần, chỉ khác thứ tự vào group. Lần thứ nhất ba consumer nhận partition là c1, c10, c2; lần thứ hai là c1, c2, c3. Chạy lại roundrobin cũng ra bộ khác với lần trước. Nghĩa là thứ tự nội bộ không suy ra được từ tên client.

Đừng thiết kế dựa trên danh tính Một hệ thống dựa vào việc “consumer số 1 luôn đọc partition 0” sẽ chạy đúng trong lúc thử rồi sai sau một lần rebalance. Thứ dự đoán được là hình dạng của phép chia — bao nhiêu partition mỗi người, có ai trắng tay không — và đó cũng là thứ duy nhất đáng dùng để định cỡ.

Phòng thí nghiệm

Tự đặt topic, số partition và số consumer, rồi đổi chiến lược để so. Thuật toán là bản viết lại của ba assignor và đã tái hiện đúng tập phần chia ở 16 kịch bản đo trên Kafka 4.3.1.

Ai đọc partition nào

    Cấu hình

    Nhớ gì

    Bài trước giải thích nửa còn lại: khoá nào rơi vào partition nào.