问题:
BU的同学在使用kafka的过程中,发现自己的数据日志不符合预期,按照正常的处理流程应该会出现预期中的数据,由于代码没有变动,之前一直在线上运行,如下:
try {
Properties props = new Properties();
props.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, Constants.KAFKA_CLUSTER);
props.setProperty(ConsumerConfig.GROUP_ID_CONFIG, CONSUMER_GROUP);
props.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
"org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
"org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
log.info("start kafka: {}", props);
consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList(KafkaUtils.getTopic(TOPIC)));
ExecutorService executorService = getExecutorService();
new Thread(() -> {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
executorService.submit(() -> {
handle(record);
});
}
}
}, "status-frame-base").start();
} catch (Exception e) {
log.error("ctr compute error", e);
}
分析:
首先想到的就是kafka是不是除了什么问题,通过matrix的监控图没有看到有什么异常
然后又查看了km上的topic相关监控信息,也没有相关的异常
所有的consumer监控指标正常,这下就懵逼中。。。
接下来,难道是consumer的poll、offset提交阻塞?
最后想consumer是不是阻塞了,然后赶紧查看系统的线程栈信息
发现没有blocked的线程
然后查看kafka的相关线程
在查找“status-frame-base”这个线程信息的时候(这个时候就知道为什么要让你们在线上起线程名字的时候为啥要规范了),竟然没有找到????
对于如此诡异的现象能做的就是查看warn和error日志
无奈没有相关的信息,再加上项目的权限等相关的问题,我拿不到相关数据,因此只能让BU的同学重启试一下了
结果:
果然重启能够解决90%的问题
回到上面的排查过程,其实通过km的监控去看consumer的信息是乏力的,因为没有相关的server到consumer心跳监控信息。我们索能看到的都是写静态的监控信息,这就是为啥在server端的监控啥也看不到
后面将kafka的相关的线程通过jmx监控在matrix里,后续排查就方便多了
后续继续跟进。。。
对了上面的代码还有一个要注意的地方,就是有些同学喜欢在使用try-catch的时候不自觉的遵守java编码规范的中的精简之道,估计是没有深入的理解这个try-catch-resource的作用
这个表达式作用在所有实现Closeable接口的资源上
1.KafkaConsumer源码实现了Consumer接口
2.Consumer接口实现了Closeable
下面错误代码示例那么写:try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)),怎会在try-catch那个代码块运行完之后被释放掉,这就是为啥也会消费不到数据
记得在没毕业那会儿实习的时候写C#遇见过,当时用C#写了一个连接SqlServer数据库获取数据的,用了lambda表达式进行简写,遭遇获取不到数据的尴尬
try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)) {
consumer.subscribe(Collections.singletonList(TOPIC));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
System.out.println(record);
}
}
}
} catch 。。。
//KafkaConsumer源码实现了Consumer接口
public class KafkaConsumer<K, V> implements Consumer<K, V> {
private static final long NO_CURRENT_THREAD = -1L;
private static final AtomicInteger CONSUMER_CLIENT_ID_SEQUENCE = new AtomicInteger(1);
……
//Consumer接口实现了Closeable
public interface Consumer<K, V> extends Closeable {
Set<TopicPartition> assignment();
Set<String> subscription();
……
下期分享一下线上的kafka consumer和producer的监控