3.4.1 生产者分区机制
Kafka消息系统为什么要进行Topic的分区呢?我们都知道Kafka的主题Topic是由分区组成的,而将Topic进行分区的主要目的就是提供负载均衡和容错的能力,以及实现系统的高伸缩性和高可用性。Kafka的消息组织方式实际上是三层结构:主题—分区—消息。主题下的每条消息只会保存在某一个分区中,而不会在多个分区中保存多份。在创建Topic的时候可以指定每个分区的副本数,用于支持分区中消息的容错。图3.3是Kafka官方网站上的截图,展示了Kafka消息模型的三层结构。
图3.3 Kafka消息模型的三层结构
不同的分区能够放置在Kafka集群中不同的节点上,而生产者和消费者在产生消息和消费消息的时候,也都是针对分区进行的,这样每个节点的机器都能独立执行各自分区的读写请求处理,并且还可以通过添加新的Kafka节点来增加整体系统的吞吐量。
既然Kafka提供了分区的机制,那么Kafka又为我们提供了哪些分区的策略呢?所谓的分区策略是决定生产者将消息发送到哪个分区的算法。Kafka生产者的分区策略都实现了接口org.apache.kafka.clients.producer.Partitioner,常见的分区策略主要有几下几种。
(1)默认分区策略(org.apache.kafka.clients.producer.internals.DefaultPartitioner)。
下面的注释摘至源码中的说明。
从注释的说明可以看出,默认的分区机制将按照以下的逻辑进行分区。
• 如果记录中指定了分区,则可以直接使用。
• 如果记录中未指定分区,但指定了key值,则根据key的hash值选择一个分区。这种策略的本质就是Hash分区。
• 如果记录中未指定分区,也未指定key值,则以黏性分区策略选择一个分区。
下面的partition方法实现了Kafka默认的分区机制。
(2)轮询分区策略(org.apache.kafka.clients.producer.RoundRobinPartitioner)。
如果key值为null,并且使用了默认的分区器,Kafka会根据轮询(Random Robin)策略将消息均匀地分布到各个分区上。下面是RoundRobinPartitioner的核心代码。
(3)黏性分区策略(org.apache.kafka.clients.producer.UniformStickyPartitioner)。
黏性分区策略就像黏住这个分区一样,只要这个分区没有被填满,就会尽可能地坚持使用该分区。这种策略首先会选择单个分区发送所有无key的消息,一旦这个分区已填满,黏性分区策略就会随机选择另一个分区。
通过查看源码,可以得到黏性分区策略是通过org.apache.kafka.clients.producer. internals.StickyPartitionCache来实现的,下面展示了StickyPartitionCache中的核心代码。
(4)散列分区策略。
如果键值不为null,并且使用了默认的分区器,Kafka会对键进行散列,然后根据散列值把消息映射到对应的分区上。下面的代码是在DefaultPartitioner中使用的散列分区。
(5)自定义分区策略。前面提到Kafka生产者的分区策略都实现了接口org.apache.kafka.clients. producer.Partitioner,用户可以根据需要对数据使用不一样的分区策略,只需要实现该接口即可。用户创建了自定义分区策略后,只需要在生产者的Properties中指定ProducerConfig.PARTITIONER_CLASS_CONFIG参数即可,代码如下所示。
下面我们通过一个具体的例子来实现Kafka生产者的自定义分区。在发送到Kafka系统的消息中,key将包含员工所在的部门号,这里将根据部门号来建立分区。例如,10号部门的员工数据将发送到0号分区;20号部门的员工数据将发送到1号分区;30号部门的员工数据将发送到2号分区;其他部门的员工数据将发送到3号分区。
完整的代码如下。
自定义分区策略创建完成后,将其加入生产者的配置参数中。