Apache kafka Kafka循环分区器未将消息分发到所有分区

Apache kafka Kafka循环分区器未将消息分发到所有分区,apache-kafka,kafka-producer-api,round-robin,partitioner,Apache Kafka,Kafka Producer Api,Round Robin,Partitioner,我试图使用Kafka的RoundRobinPartitioner类在所有分区上均匀地分发消息。我的卡夫卡主题配置如下: 名称:multischemakafkatopicodd 分区数目:16 复制因子:2 比方说,如果我生成100条消息,那么每个分区应该有6或7条消息。但是,我得到了类似的结果: sh /usr/hdp/current/kafka-broker/bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list 10.0

我试图使用Kafka的RoundRobinPartitioner类在所有分区上均匀地分发消息。我的卡夫卡主题配置如下:

名称:multischemakafkatopicodd

分区数目:16

复制因子:2

比方说,如果我生成100条消息,那么每个分区应该有6或7条消息。但是,我得到了类似的结果:

sh /usr/hdp/current/kafka-broker/bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list 10.0.55.211:9092 --topic multischemakafkatopicodd --time -1
multischemakafkatopicodd:0:26
multischemakafkatopicodd:5:0
multischemakafkatopicodd:10:24
multischemakafkatopicodd:15:0
multischemakafkatopicodd:13:0
multischemakafkatopicodd:8:26
multischemakafkatopicodd:2:26
multischemakafkatopicodd:12:24
multischemakafkatopicodd:14:24
multischemakafkatopicodd:9:0
multischemakafkatopicodd:11:0
multischemakafkatopicodd:4:26
multischemakafkatopicodd:1:0
multischemakafkatopicodd:6:24
multischemakafkatopicodd:7:0
multischemakafkatopicodd:3:0

我认为这可能是因为我没有生成足够的消息,所以我尝试使用1M记录,并将分区数设置为奇数:

主题:多模式

分区数目:31

复制因子:2

…我得到了这个。这一次,每个分区中的消息数量在某种程度上是均匀分布的

sh /usr/hdp/current/kafka-broker/bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list 10.0.55.211:9092 --topic multischemakafkatopicodd --time -1
multischemakafkatopicodd:0:33845
multischemakafkatopicodd:5:34388
multischemakafkatopicodd:10:33837
multischemakafkatopicodd:20:33819
multischemakafkatopicodd:15:33890
multischemakafkatopicodd:25:34414
multischemakafkatopicodd:30:33862
multischemakafkatopicodd:26:34066
multischemakafkatopicodd:9:34088
multischemakafkatopicodd:11:34124
multischemakafkatopicodd:16:33802
multischemakafkatopicodd:4:34061
multischemakafkatopicodd:17:34977
multischemakafkatopicodd:3:34084
multischemakafkatopicodd:24:33849
multischemakafkatopicodd:23:34111
multischemakafkatopicodd:13:34062
multischemakafkatopicodd:28:33876
multischemakafkatopicodd:18:34098
multischemakafkatopicodd:22:34058
multischemakafkatopicodd:8:34079
multischemakafkatopicodd:2:33839
multischemakafkatopicodd:12:34075
multischemakafkatopicodd:29:34132
multischemakafkatopicodd:19:33924
multischemakafkatopicodd:14:34109
multischemakafkatopicodd:1:34088
multischemakafkatopicodd:6:33832
multischemakafkatopicodd:7:34080
multischemakafkatopicodd:27:34188
multischemakafkatopicodd:21:34684
我再次进行了相同的测试,但将分区的数量减少到了8个,我得到了这样的结果:我们可以清楚地看到,一些分区有接近15K的消息,而另一些分区有大约10K的消息:

multischemakafkatopicodd:0:155927
multischemakafkatopicodd:5:105351
multischemakafkatopicodd:1:107382
multischemakafkatopicodd:4:160533
multischemakafkatopicodd:6:158007
multischemakafkatopicodd:7:105608
multischemakafkatopicodd:2:157934
multischemakafkatopicodd:3:105599
我做错了什么吗?或者这就是它的工作原理? 为什么信息分布如此不平等


如果有人能帮我,那就太好了。谢谢。

据我所知,分区器工作正常。但您必须了解生产商为了最大限度地提高性能所做的优化:

  • 生产者不会为每个发送调用将每条消息生成到不同的分区,因为这样做会造成过度杀伤力

  • 循环
    保证类似的分发,但可以发送批。这意味着,它将根据
    RoundRobinPartitioner
    的代码中执行的(而不是模数!)操作来缓冲一定数量的发送到分区的消息:

     int part = Utils.toPositive(nextValue) % availablePartitions.size();
    
nextValue
是一个
AtomicInteger
,它为每个分区/发送调用增加1。因此,如果在该过程中没有分区被声明为不可用,那么剩余部分也将始终以1的增量递增(以循环方式递增,例如使用4个分区:
0-1-2-3-0-1-2-3-…
)。如果发生这种情况,循环可能看起来像
0-1-2-(partition4fails)-0-1-2-(partition4OK)-3-0-…


例子
  • 带有4个分区的主题
  • 每个分区的生产者分区器线程的缓冲区包含3条消息
(消息编号计数器以0开始-
新的原子整数(0)

当生成第9条消息时,第一个分区的缓冲区已经完成(因为它已经保存了3条消息),因此准备发送给kafka。如果在此处停止进程,则4个分区将如下所示:

    Partition    Offset
       0           3
       1           0
       2           0
       3           0
当生成第10条消息时,第二个分区的缓冲区也将准备就绪,可以从线路中发送,主题如下:

    Partition    Offset
       0           3
       1           3
       2           0
       3           0
    Partition    Offset
       0           1000
       1           1000
       2           0
       3           0
    Partition    Offset
       0           2997
       1           1996
       2           999
       3           998
在现实生活中,缓冲区通常包含大量的消息(这也可以被挖掘)。比如说,存储了1000条消息。对于相同的场景,分区如下所示:

    Partition    Offset
       0           3
       1           3
       2           0
       3           0
    Partition    Offset
       0           1000
       1           1000
       2           0
       3           0
    Partition    Offset
       0           2997
       1           1996
       2           999
       3           998
因此,增加分区之间的“视觉”差异。批处理大小/缓冲区大小越大,问题就越严重

这与生产者的
partitioner
线程本身的性质有关:默认情况下,它不会独立地发送每条消息,而是存储它们,以便在每次代理调用时都能发送消息,从而优化系统性能

批处理是提高效率的主要驱动因素之一,并使 批处理卡夫卡制作者将尝试在内存中累积数据 并在单个请求中发送更大的批次

如果停止/启动生产者,这种不平衡可能会更加臭名昭著,因为它将重新启动机制,而不管先前选择的分区是什么(因此它可以开始发送到停止前刚刚选择的同一个分区,从而增加上次执行时与其他未选择分区的差异)

在新的执行中,缓冲区将全部为空,因此无论哪个分区接收最多,进程都将重新启动

因此,您可以在此处停止此过程:

    Partition    Offset
       0           1000
       1           1000
       2           0
       3           0
保存每个主题的消息数计数器的映射将重新启动,因为它不是来自生产者的代理类的一部分。如果生产者未正确关闭和/或刷新,则缓存的消息也将丢失。因此,在这个场景中,您得到的是前面逻辑的重复:

    MsgN % Partitions   Partition
        0%4                0
        1%4                1
        2%4                2
        3%4                3
                 (...)
这将在某个时刻导致这种情况:

    Partition    Offset
       0          2000
       1          2000
       2           0
       3           0
这是由于发送进程的非连续执行而产生的不平衡,但它超出了
RoundRobinPartitioner
的范围,后者的本质是基于连续进程(不间断)

您可以通过在发送消息时检查每个分区的偏移量来验证此行为:仅当所选分区存储n消息时,下一个选中的分区将获得其批n消息

注:示例中显示的数字表示“完美”场景;在现实生活中,消息也可以被撤销、压缩、失败、刷新,而不管缓冲区大小、分区不可用等等,。。。导致偏移数,如问题中所示

最后一个刷新场景示例:

    Partition    Offset
       0           1000
       1           1000
       2           0
       3           0
进程已停止,但生产者已正确关闭并刷新其消息,因此主题如下:

    Partition    Offset
       0           1997
       1           1996
       2           999
       3           998
进程将重新启动。刷新第一个分区的缓冲区后,将如下所示:

    Partition    Offset
       0           3
       1           3
       2           0
       3           0
    Partition    Offset
       0           1000
       1           1000
       2           0
       3           0
    Partition    Offset
       0           2997
       1           1996
       2           999
       3           998
因此,增加了对该机制“公平”的困惑。但这不是它的错,因为分区器的映射、计数器和缓冲区中没有持久性。如果让流程连续几天不停地执行,您会发现它确实以“近乎相等”的方式平衡了消息


'sreleva