kafka consumer多线程消费

680 阅读2分钟

需求

一个给客户实时回传数据的需求,需要从kafka消费并http发送给客户,采用了kafka consumer多线程消费的方式。

多线程方案

首先,kafkaConsumer类不是线程安全的,不能在多线程中共享kafkaConsumer实例,否则会抛出异常。但是KafkaConsumer 中有个方法是例外的,它就是 wakeup(),你可以在其他线程中安全地调用 KafkaConsumer.wakeup() 来唤醒 Consumer。 目前有两种常见的consumer多线程消费方案:

  1. 启动多个kafkaconsumer实例(线程),进行完整的消费,处理流程。
  2. 启动多个kafkaconsumer实例(线程),同时使用多个线程进行消息处理流程。

第一种方式会受到kafka topic分区数的限制,第二种实现难度高,单分区无法保证消费顺序。由于我们的线上topic有160个分区,评估分区数应该不是限制,所以采用第一种方案。

image.png

Rebalance

consumer group重平衡的问题在哪种方案中都难以避免,重平衡过程中所有消费者都暂停了消费,开销很大。那么我们是否可以避免重平衡问题呢?

重平衡有三个触发的原因:

  1. 消费者组数量发生变化
  2. 订阅topic数量发生变化
  3. 订阅topic的分区数发生变化

一般来讲给后面两种情况是主动操作,也是难以避免的,所以主要关注第一种情况出现的原因:

  1. 没有及时发送心跳,导致consumer被踢出group。
    session.timeout.ms:推荐设置为6s consumer存活的时间间隔。
    heartbeat.interval.ms: 推荐设置为2s 发送心跳的间隔。目前Coordinator通知各个Consumer实例开启 Rebalance 的方法,就是将 REBALANCE_NEEDED 标志封装进心跳请求的响应体中。
  2. Consumer消费时间过长,会被踢出group,提交位移会抛出CommitFailedException。如果是本身正常消费时间就大于max.poll.interval.ms,最后会导致所有consumer都被踢出
    max.poll.interval.ms:两次poll之间的时间间隔
    max.poll.records:一次拉取的最大record数量

实现

在实际使用中,rebalance前要提交系统的offset,rebalance后要更新分配的分区

consumer.subscribe(Arrays.asList(topic), new ConsumerRebalanceListener() {
    @Override
    public void onPartitionsRevoked(Collection<TopicPartition> collection) {
        logger.info("before rebalance,commit offset");
        consumer.commitSync();//同步提交位移

    }

    @Override
    public void onPartitionsAssigned(Collection<TopicPartition> collection) {
        logger.info("rebalance after");
        init();//更新分配的分区
    }
});