Kafka注册重(chong二声)平衡监听

256 阅读2分钟

前言

kafka消费时,一般情况下不需要关注消费者是否进行了重平衡。但是在一些特定的业务场景,我们不希望消费者进行重平衡,这时除了修改配置之外,还需要知道消费者什么时候进行了重平衡,方便我们进行代码优化。

为什么要重平衡

-首先kafka为什么要重平衡?这里列举两个常见的场景:

  1. 有消费者节点挂掉,剩下的消费者线程需要进行重新分配消费分区;
  2. 超时之前还未收到心跳信息,这时kafka会将此消费者注销,再重新分配;

重平衡的影响

  1. 数据重复消费: 消费过的数据由于提交offset任务也会失败,在partition被分配给其他消费者的时候,会造成重复消费,数据重复且增加集群压力;
  2. Rebalance扩散到整个ConsumerGroup的所有消费者,因为一个消费者的退出,导致整个Group进行了Rebalance,并在一个比较慢的时间内达到稳定状态,影响面较大;
  3. 频繁的Rebalance反而降低了消息的消费速度,大部分时间都在重复消费和Rebalance;
  4. 数据不能及时消费,会累积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();