建一个3分区topic,用3个key各发10条严格编号的消息,验证同一个key的消息在分区内部是否严格有序,以及consumer看到的跨分区全局到达顺序是否等于发送顺序。
命令 · python KEYS = ["user-A", "user-B", "user-C"] for seq in range(10): for k in KEYS: p.produce("order-demo", key=k.encode(), value=f"{k}:{seq}".encode()) p.flush() # 每一轮都flush,三个key的第seq条消息真实按接近同时的顺序发出
order-demo真实的分区数: 3 [0, 1, 2]
分区0: 真实包含的key={'user-C'}, 前5条=['user-C:0', 'user-C:1', 'user-C:2', 'user-C:3', 'user-C:4']
真实序号序列: [0,1,2,3,4,5,6,7,8,9] , 是否严格递增: True
分区1: 真实包含的key=set(), 前5条=[]
真实序号序列: [] , 是否严格递增: True
分区2: 真实包含的key={'user-B', 'user-A'}, 前5条=['user-A:0', 'user-B:0', 'user-A:1', 'user-B:1', 'user-A:2']
真实序号序列: [0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9] , 是否严格递增: True真实结果比预想的更能说明问题:user-A和user-B这两个不同的key,被真实的哈希函数分到了同一个分区(分区2),而分区1完全没有收到任何消息——分区分配是按key哈希取模决定的,不是"雨露均沾"式的轮询,只要key的数量不够多、运气不好,真实就会出现哈希碰撞和空分区。分区2内部虽然混着user-A和user-B两个key的消息,但它们真实还是按照producer发送的先后顺序严格排列(每一轮的user-A排在user-B前面,因为代码里就是这么发的),这就是"分区内部有序"真实的含义:有序性是针对这个分区上追加的全体消息,不是针对某一个key单独排的队。
真实消费者看到的前15条全局到达顺序(分区号, 内容): (0, 'user-C:0') (0, 'user-C:1') ...(分区0的全部10条)... (0, 'user-C:9') (2, 'user-A:0') (2, 'user-B:0') (2, 'user-A:1') (2, 'user-B:1') (2, 'user-A:2')
每一步都是本文上面用3分区topic跑出来的原始结果。
Kafka真实的顺序保证严格限定在"同一个分区内部":同一个key的消息永远落在同一个分区(因为分区号是key哈希取模算出来的),这个分区上的消息永远按追加顺序排列,consumer读这个分区也永远按这个顺序读——这条链路上每一环都真实成立。但只要涉及多个分区(不同的key,或者同一个topic的整体视角),Kafka不做任何跨分区的顺序承诺,consumer看到的到达顺序真实只取决于当时的网络和调度节奏。