前言
kafka消费时,一般情况下不需要关注消费者是否进行了重平衡。但是在一些特定的业务场景,我们不希望消费者进行重平衡,这时除了修改配置之外,还需要知道消费者什么时候进行了重平衡,方便我们进行代码优化。
为什么要重平衡
-首先kafka为什么要重平衡?这里列举两个常见的场景:
- 有消费者节点挂掉,剩下的消费者线程需要进行重新分配消费分区;
- 超时之前还未收到心跳信息,这时kafka会将此消费者注销,再重新分配;
重平衡的影响
- 数据重复消费: 消费过的数据由于提交offset任务也会失败,在partition被分配给其他消费者的时候,会造成重复消费,数据重复且增加集群压力;
- Rebalance扩散到整个ConsumerGroup的所有消费者,因为一个消费者的退出,导致整个Group进行了Rebalance,并在一个比较慢的时间内达到稳定状态,影响面较大;
- 频繁的Rebalance反而降低了消息的消费速度,大部分时间都在重复消费和Rebalance;
- 数据不能及时消费,会累积lag,在Kafka的TTL之后会丢弃数据;
- 自定义Spring注册重平衡监听,自定义的监听中,可以输出日志,也可以做其他处理,比如邮件通知等;
@Component
@Slf4j
public class KafkaConfigBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 注册重平衡监听
if (bean instanceof KafkaListenerContainerFactory) {
((ConcurrentKafkaListenerContainerFactory) bean)
.getContainerProperties()
.setConsumerRebalanceListener(new ConsumerRebalanceListener() {
@Override
// 撤销分区消费者
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
log.error("REBALANCE,revoked,partition:{}", partitions);
}
@Override
// 分配分区消费者
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
log.error("REBALANCE,assigned,partition:{}", partitions);
}
});
}
return bean;
}
}
默认监听类
当然,如果我们不进行自定义的重平衡监听器,Spring-kafka还会给我们分配一个默认的,我们来看看源码AbstractMessageListenerContainer.class中第128行,如果containerProperties中的ConsumerRebalanceListener没有设置,那么会设置一个SimpleLoggingConsumerRebalanceListener监听类
if (this.containerProperties.getConsumerRebalanceListener() == null) {
this.containerProperties.setConsumerRebalanceListener(createSimpleLoggingConsumerRebalanceListener());
}
SimpleLoggingConsumerRebalanceListener监听类源码,其中也是没有做什么处理,仅仅输出了日志:
/**
* Return default implementation of {@link ConsumerRebalanceListener} instance.
* @return the {@link ConsumerRebalanceListener} currently assigned to this container.
*/
protected final ConsumerRebalanceListener createSimpleLoggingConsumerRebalanceListener() {
return new ConsumerRebalanceListener() {
@Override
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
AbstractMessageListenerContainer.this.logger.info("partitions revoked: " + partitions);
}
@Override
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
AbstractMessageListenerContainer.this.logger.info("partitions assigned: " + partitions);
}
};
}
监听类使用的地方
KafkaMessageListenerContainer
378行
内部类 ListenerConsumer
1608行
内部类 ListenerConsumerRebalanceListener
ConsumerRebalanceListener userListener = getContainerProperties().getConsumerRebalanceListener();