分区和主题
一般我们会在一个topic下设置多个分区,这样多个分区对应多个消费者,以此可以提高kafka的吞吐量,相当于处理消息时达到了多线程并行执行的效果。
分区和消息
当生产者要向某个主题发送消息时,生产者会先将消息序列化处理,然后根据topic,serializedKey,serializedValue计算出当前消息应该发送到哪个分区中,默认情况下如果没有指定key值,则按照类似轮询分区数的方式发送,如果指定了key值,则根据key值进行hash后取模发送,所以同一个key值得消息会始终被发送到一个分区中,也就是会始终被一个消费者消费到(考虑可以实现消息的顺序性,但同时会造成资源分配不均衡,降低消息处理的能力)。
发送时指定key的情况下
往topic为"test_topic"的主题中,指定key值,生产10条消息
生产消息
for (int i = 0; i < 10; i++) {
kafkaTemplate.send("test_topic", "test_topic_key", String.valueOf(i));
}
接收消息
@KafkaListener(topics = {"test_topic"})
public void listen(ConsumerRecord<?, ?> record) {
logger.info("get msg key: {}, value: {}, partition: {}", record.key(), record.value().toString(), record.partition());
}
一共有4个分区
从结果可以看出,指定了key的情况下,所有的消息都发送到了分区0上
如果发送时不指定key
从结果可以看出,如果不指定key的情况下,有的消息在分区1上,有的消息在分区3上。
kafka自定义分区器
如果有时候我们在发送消息需要通过key值来进行一些业务上的划分,但key又会造成消息始终只能发送的一个消费者中,导致处理能力降低,那么这时候就可以通过自定义分区的方式,让有key值的消息也能按照比较均衡的方式发送消息。
自定义分区器
public class MySelfPartitioner implements Partitioner {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
public int partition(String topic, Object key, byte[] keyBytes,
Object value, byte[] valueBytes, Cluster cluster) {
logger.info("use MySelfPartitioner ...");
//拿到主题中的分区信息
List<PartitionInfo> partitionInfos = cluster.partitionsForTopic(topic);
//获取分区数
int num = partitionInfos.size();
//计算value的hashcode,然后取模,获取一个分区
int parId = value.hashCode()%num;
return parId;
}
public void close() {
}
public void configure(Map<String, ?> configs) {
}
}
新增一个配置属性
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
props.put(ProducerConfig.RETRIES_CONFIG, retries);
props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);
props.put(ProducerConfig.LINGER_MS_CONFIG, linger);
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
//设置自定义分区器
props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, "com.wyl.config.MySelfPartitioner");
return props;
}
可以看出,虽然指定了key,但是已经明显不是一个分区了,而是根据value取模计算后获取对应分区
分区和消费者的对应关系
- 当分区和消费者数量相同时,一个分区对应一个消费者。
- 当分区数大于消费者数时,那么会出现一个消费者对应多个分区。
- 当分区数小于消费者数时,多出来的消费者将不会消费任何消息。
分区再均衡
当我们消费者数量发生变化,或者分区数发生变化,那么kafka就需要重写计算分区和消费者的对应关系,这种现象就叫做分区再均衡。一般情况下要尽量避免分区再均衡的发生,因为这会造成消费者无法读取消息,当时消费者会通过心跳来和broker保持连接,如果消费者长时间不发送心跳那么broker就为认为这个消费者已经挂掉了,那么也会造成分区再均衡的发生。