这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战
Kafka 的消息数据的组织方式分一下几个层次:
- Topic,可以理解为一个容器,用来存放同一主题的消息,这里的主题可以理解为各种不同的业务、部门、甚至是租户等。
- Partition,也叫做分区,就是把同一个 Topic 中的数据,分成几部分,保存在不同的 Kafka Broker 里,这样可以提高消息吞吐量。每一个消息只能存在于一个分区中,不会重复保存。
- Replica,也叫做副本,每一个分区可以有若干个副本,它们保存着同样的数据,保证分区的可用性。
其中,Topic 是在生产消息之前就设定好的,每个消息会固定发送到指定的 Topic;Replica 是分区数据的完整副本,只需要分区的 Leader 副本的数据变化,同步数据即可。
而分区是比较灵活的,一个消息被发送到指定的 Topic 后,要进入哪个分区,需要根据一个分区策略来计算。
轮询策略
在 Kafka 的生产者 API 中,默认的策略就是轮询策略。轮询策略就是把生产的消息,按照分区,进行顺序分配。比如一个 Topic 被分成了三个分区,那么,第一条消息进入分区0,第二条消息进入分区1,第三条消息进去分区2,第四条消息再进入到分区0,以此类推。
在实际的经验中,轮询策略总是能最大限度地平均分配消息到每一个分区中。
自定义策略
在 Kafka 中,如果要自己定义分区策略,需要修改生产者中的 partitioner.class
参数,它的值是一个 Java 类的完整名称(包含包名),这个类需要实现 org.apache.kafka.clients.producer.Partitioner
接口,这个接口中有 partition
和 close
方法,其中 partition
方法就是实现具体分区逻辑的地方,它的方法签名如下:
int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);
方法参数提供了很多信息可以用来实现计算分区的逻辑,其中 cluster 对象中甚至包含了整个集群的 Topic 数量、Broker 数量等。
随机策略
随机策略也是常见的策略之一,它的实现方式类似如下的代码:
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return ThreadLocalRandom.current().nextInt(partitions.size());
随机策略的本身意图是为了消息更平均地分布在各个分区中,但它的效果其实不如轮询策略。
根据消息 key 分区的策略
在 Kafka 中,每一则消息可以定义一个 key,这个 key 可以是客户编号、业务 ID、时间戳、交易订单号等等,根据具体业务而定。我们可以根据这个 key 进行分区逻辑的计算。
例如下面的逻辑:
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return Math.abs(key.hashCode()) % partitions.size();
根据 key 来计算分区,有一个好处,就是可以让 key 相同的消息进入到同一个分区。因为在 Kafka 中,同一个分区是可以保证顺序的,而多个分区之间是不能保证顺序的,如果你的业务需要多个消息之间保证顺序,那么,可以给他们同样的 key,然后根据 key 来计算分区,这样既可以享受分区带来的高吞吐量,也可以保证消息顺序。
其他
得益于 Partitioner
接口的分区逻辑方法的参数重提供了足够的数据,根据不同的业务场景,可以实现的分区逻辑方式有很多,比如,对于多地区集群进行根据地理位置的分区等等。