生产者消息分区机制原理
# 一、分区基本概念
kafak的消息是通过主题来划分的,而同一个主题的消息会落在不同分区上,分区内部才是真实的消息主体,再通过通过副本机制,实现消息的冗余性,来达到高可用的性能。
整体而言,Kafka 的消息组织方式实际上是三级结构:主题 - 分区 - 消息。主题下的每条消息只会保存在某一个分区中,而不会在多个分区中被保存多份。
# 二、分区策略
消息通过生产者发送到消息累加器的过程中会经过如下步骤:
- 拦截器
- 序列化器
- 分区器
其中分区器中会有分区策略
,所谓分区策略
是决定生产者将消息发送到哪个分区的算法。kafka提供了默认的分区策略,同时也支持自定义分区策略。常见的分区策略主要有如下几种:
# 1、轮询策略
Round-robin 策略,即顺序分配。比如一个主题下有 3 个分区,那么第一条消息被发送到分区 0,第二条被发送到分区 1,第三条被发送到分区 2,依此类推,第四条消息就被发送到分区0。
# 2、随机策略
Randomness 策略。所谓随机就是我们随意地将消息放置到任意一个分区上:
果要实现随机策略版的 partition 方法,很简单,只需要两行代码即可:
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return ThreadLocalRandom.current().nextInt(partitions.size());
本质上看随机策略也是力求将数据均匀地打散到各个分区,但从实际表现来看,它要逊于轮询策略,所以如果追求数据的均匀分布,还是使用轮询策略比较好。事实上,随机策略是老版本生产者使用的分区策略,在新版本中已经改为轮询了。
# 3、消息键保序策略
Kafka 允许为每条消息定义消息键,简称为 Key。一旦消息被定义了 Key,那么你就可以保证同一个 Key 的所有消息都进入到相同的分区里面,由于每个分区下的消息处理都是有顺序的,故这个策略被称为按消息键保序策略,如下图所示:
实现这个策略的 partition 方法同样简单,只需要下面两行代码即可:
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return Math.abs(key.hashCode()) % partitions.size();
前面提到的 Kafka 默认分区策略实际上同时实现了两种策略:
- 如果指定了 Key,那么默认实现按消息键保序策略;
- 如果没有指定 Key,则使用轮询策略。
# 三、如何保证消息的顺序性
# 1、单分区策略
由于kafka会把同一个主题消息发送到不同分区,而不同分区的消费速度可能不一样,这样就会导致消息消费顺序的不一致。
一种做法是Kafka 主题设置单分区,也就是 1 个分区。这样所有的消息都只在这一个分区内读写,因此保证了全局的顺序性。这样做虽然实现了因果关系的顺序性,但也丧失了 Kafka 多分区带来的高吞吐量和负载均衡的优势。
# 2、key策略
由于同一个key会被发送到指定分区,我们可以对此标志位设定专门的分区策略,保证同一标志位的所有消息都发送到同一分区,这样既可以保证分区内的消息顺序,也可以享受到多分区带来的性能红利。