KafkaConsumer 长时间地在poll(long )方法中阻塞的原因与解决

一,问题描述:
搭建本地kafka环境,默认端口为9092
server.properties中默认配置为:

# advertised.listeners=PLAINTEXT://10.13.84.109:9092

消费者Demo程序如下:

    while (true) {
        if(consumer != null)
        {
            // timeout 阻塞时间,从kafka中取出100毫秒的数据,有可能一次取出0到N条
            List> datas = new ArrayList<>();
            ConsumerRecords records = consumer.poll(100);
            // 遍历
            for (ConsumerRecord record : records) {
                Map notifyDto = ( Map ) JsonUtils.jsonToMap(record.value());
                datas.add(notifyDto);
            }
            // 拿出结果
            if(CollectionUtils.isNotEmpty(datas)){
                logger.info(RunTimeLogUtil.toLog(LogObjectTypeEnum.SYSTEM,"consume","Get data platform Notify",null,null, "record"),JsonUtils.object2Json(datas));
                // 起线程处理 资源变更通知
                resourceHandle(datas);
            }
        } else
        {
            break;
        }
    }

在创建consumer对象端口为9093后,进入consumer.poll(100)是发生堵塞,线程直接卡在poll中,设定的100ms的超时也无效。

private void handlerConsumer(String kafkaIp, String kafkaPort) {
    Properties props = new Properties();
    props.setProperty("bootstrap.servers", kafkaIp + ":" + kafkaPort);
    // key反序列化
    props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
    // value反序列化
    props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
    // 每个消费者都必须属于某一个消费组,所以必须指定group.id
    props.put("group.id", "test");
    // 构造消费者对象
    deviceNoifyThreadExecutor.execute(()->{
        KafkaConsumer<String, String> consumerObj = null;
        // 指定多主题:
        List<String> topics = CbdmOptUtil.stringToStringList(PropertiesUtil.getProperty("kafka.subscribe.topics"), ConstParamErrorCode.DEFAULT_SPLIT_KEY, false);
        try {
            consumerObj = new KafkaConsumer<>(props);
            if(consumerObj != null) {
                consumerObj.subscribe(topics);
                resourceNotifyConsumer.setConsumer(consumerObj);
                KafkaLinkCache.DEVICE_CONSUMER_MAP.put("kafkaComsumer", consumerObj);
                resourceNotifyConsumer.onMessage();
            }
        } catch(Exception e) {
            LogUtils.logError(RunTimeLogUtil.toErrorLog(ConstParamErrorCode.SYSTEM_CODE_FAIL + "", LogObjectTypeEnum.SYSTEM,"consume",
                    "resolve data platform notify error"),e);
        }finally {
            // 关闭
            consumerObj.close();
        }
    });

    //保存配置
    KafkaLinkCache.kafkaConfigCache = kafkaIp + "_" + kafkaPort;
}

二,问题原因:
在poll(0)中consumer会一直阻塞直到它成功获取了所需的元数据信息,之后它才会发起fetch请求去获取数据。虽然poll可以指定超时时间,但这个超时时间只适用于后面的消息获取,前面更新元数据信息不计入这个超时时间。所以,设定的timout时间失效。poll(0)这种设计的一个问题在于如果远端的broker不可用了, 那么consumer程序会被无限阻塞下去。用户指定了超时时间但却被无限阻塞,显然这样的设计时有欠缺的。特别是对于Kafka Streams而言,这个设计可能导致的问题在于Stream Thread无法正常关闭。目前源代码中依然有一些无限阻塞的场景,比如之前处理的initTransaction,commitTransaction和abortTransaction也是无限等待。

三,问题解决:
poll(Duration)这个版本修改了这样的设计,会把元数据获取也计入整个超时时间,从而避免了无效等待。

ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));

你可能感兴趣的:(kafka,java,分布式,zookeeper)