kafka多线程消费及处理和手动提交处理方案设计

本文介绍了Kafka消费者在0.9版本后的特性,特别是手动提交和多线程消费的使用。针对单线程poll可能导致的长时间处理导致心跳延迟和offset未提交的问题,设计了一个方案,通过限制`max.poll.records`,分离消息poll和处理,以及使用单独线程按一定条件提交offset,确保心跳正常和避免消息重复消费。同时,指出了该设计存在的不足,即分区调整时处理任务和线程的管理问题。

kafka与其他消息队列不同的是, kafka的消费者状态由外部( 消费者本身或者类似于Zookeeper之类的外部存储 )进行维护, 所以kafka的消费就更加灵活, 但是也带来了很多的问题, 因为客户端消费超时被判定挂掉而消费者重新分配分区, 导致重复消费, 或者客户端挂掉而导致重复消费等问题.

本文内容简介

kafka的消费者有很多种不同的用法及模型. * 本文着重探讨0.9版本及之后的kafka新consumer API的手动提交和多线程的使用* . 对于外部存储offset, 手动偏移设置, 以及手动分区分配等不同消费者方案, 将在其他文章中介绍.

消费者在单线程下的使用

下面介绍单线程情况下自动提交和手动提交的两种消费者

1. 自动提交, 单线程poll, 然后消费

        Properties props = new Properties();
        props.put("bootstrap.servers", servers);
        props.put("group.id", "autoCommitGroup");
        //自动提交
        props.put("enable.auto.commit", "true");
        //自动提交时间间隔
        props.put("auto.commit.interval.ms", "1000");
        //key和value的序列化类
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList(topic));
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);
            for (ConsumerRecord<String, String> record : records)
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
        }

offset自动提交会让人产生误会, 其实并不是在后台提交, 而是在poll时才会进行offset提交.

2. 手动提交, 单线程poll, 读取一定量的数据后才提交offset

        Properties props = new Properties();
        props.put("bootstrap.servers", servers);
        props.put("group.id", "manualOffsetControlTest");
        //手动提交
        props.put("enable.auto.commit", "false");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList(topic));
        //每次处理200条消息后才提交
        final int minBatchSize = 200;
        //用于保存消息的list
        ArrayList<ConsumerRecord<String, String>> buffer = new ArrayList<>();
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);
            for (ConsumerRecord<String, String> record : records) {
                buffer.add(record);
            }
            //如果读取到的消息满了200条, 就进行处理
            if (buffer.size() >= minBatchSize) {
                doSomething(buffer);
                //处理完之后进行提交
                consumer.commitAsync();
                //清除list, 继续接收
                buffer.clear();
            }
        }

新kafka消费者的版本特性

在接下来的探讨之前, 需要简单介绍一下kafka消费者的特性.

  1. kafka的0.9版本中重写了consumer API
  2. consumer维护了消费者当前消费状态, 不是线程安全的
  3. 新的consumer基于单线程模型, offset自动提交在poll方法中进行, 0.9–0.10.0.1, 客户端的心跳也是在poll中进行, 在0.10.1.0版本中, 客户端心跳在后台异步发送了
  4. 0.9版本不能设置每回poll返回的最大数据量, 所以poll一次会返回上一次消费位置到最新位置的数据, 或者最大的数据量. 在0.10.0.1版本及之后, 可以通过在consumer的props中设置max.poll.records来限制每回返回的最大数据条数.

我的设计

我所使用的kafka版本是0.10.0.1, 所以使用的是新版本的consumer API, 可以限制每回返回的最大数据

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值