如何使消息在所有分区上均匀分布

时间:2018-08-31 03:05:54

标签: apache-kafka

enter image description here我使用kafka,将消息发送到kafka代理,我的分区号是24,我想在24个分区上均匀地发送消息。 现在我的钥匙就像,

String topicName="data_"+region;
JSONObject jsonObject = JSON.parseObject(json);
Random rand = new Random();
int  n = rand.nextInt(50) + 1;
ListenableFuture<SendResult<Integer, String>> result =kafkaTemplate.send(topicName,type+n,jsonObject.toJSONString());

但是这些消息不是均匀分布的。 如何设计我的钥匙?哈希值或其他? 感谢您的所有建议!

1 个答案:

答案 0 :(得分:2)

好吧,简短的答案是密钥(type+n)的前缀是罪魁祸首。但为什么?好吧,我不确定,因为我今天离开数学家:-)

尽管如此,我们还是来看看吧!当您将密钥用于记录时(强烈建议您使用记录压缩,因为您以后可能要依赖日志压缩),并且用Java或Spring Kafka编写应用程序时,记录将在其中终止的分区由Kafka Java库确定。更具体地说,决策者是org.apache.kafka.clients.producer.Partitioner的默认实现。此实现为org.apache.kafka.clients.producer.internals.DefaultPartitioner。参见here

以下是实际计算分区的方式:

return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;

使用的哈希函数为murmur2。让我们写一个简短的代码段,如果您给键加上前缀,它将模拟24个分区上的10K记录的分布(就像您一样):

Random rand = new Random();

Map<Integer, Integer> distro = new HashMap<>();
    for (int i = 0; i < 10000; i++) {
      int n = rand.nextInt(50) + 1;

      int partition = Utils.toPositive(Utils.murmur2(("type_" + String.valueOf(n)).getBytes())) % 24;
      int cnt = distro.getOrDefault(partition, 0) + 1;

      distro.put(partition, cnt);
    }

    distro.entrySet().forEach(e-> System.out.println("Partition= " + e.getKey() + " Entries= " + e.getValue()));

这是您遇到的不良分布:

Partition= 2 Entries= 180
Partition= 4 Entries= 388
Partition= 5 Entries= 813
Partition= 6 Entries= 1438
Partition= 7 Entries= 572
Partition= 9 Entries= 791
Partition= 10 Entries= 1036
Partition= 12 Entries= 815
Partition= 14 Entries= 184
Partition= 15 Entries= 579
Partition= 16 Entries= 608
Partition= 18 Entries= 610
Partition= 19 Entries= 215
Partition= 20 Entries= 562
Partition= 21 Entries= 395
Partition= 22 Entries= 370
Partition= 23 Entries= 444

如您所见,某些分区甚至没有填充,分区10和6有点过载。现在,让我们像这样从您的 small 键中删除前缀:

int partition = Utils.toPositive(Utils.murmur2((String.valueOf(n)).getBytes())) % 24;

现在情况看起来更加统一,但这仍然不是完美的:

Partition= 0 Entries= 799
Partition= 1 Entries= 411
Partition= 3 Entries= 835
Partition= 4 Entries= 224
Partition= 5 Entries= 563
Partition= 6 Entries= 591
Partition= 7 Entries= 812
Partition= 8 Entries= 596
Partition= 10 Entries= 211
Partition= 11 Entries= 424
Partition= 12 Entries= 608
Partition= 13 Entries= 225
Partition= 14 Entries= 187
Partition= 15 Entries= 786
Partition= 16 Entries= 584
Partition= 18 Entries= 606
Partition= 19 Entries= 425
Partition= 21 Entries= 159
Partition= 22 Entries= 554
Partition= 23 Entries= 400

您可以像我们一样将UUID-s用作密钥,例如:

int partition = Utils.toPositive(Utils.murmur2(UUID.randomUUID().toString().getBytes())) % 24;

这对于murmur2来说非常顺畅:

Partition= 0 Entries= 429
Partition= 1 Entries= 407
Partition= 2 Entries= 420
Partition= 3 Entries= 435
Partition= 4 Entries= 407
Partition= 5 Entries= 421
Partition= 6 Entries= 403
Partition= 7 Entries= 460
Partition= 8 Entries= 399
Partition= 9 Entries= 415
Partition= 10 Entries= 386
Partition= 11 Entries= 402
Partition= 12 Entries= 424
Partition= 13 Entries= 434
Partition= 14 Entries= 391
Partition= 15 Entries= 426
Partition= 16 Entries= 399
Partition= 17 Entries= 430
Partition= 18 Entries= 435
Partition= 19 Entries= 418
Partition= 20 Entries= 403
Partition= 21 Entries= 418
Partition= 22 Entries= 402
Partition= 23 Entries= 436

另一种选择是增加密钥的范围,目前最多可以达到50个。