本文章内容皆出自作者阅读胡夕著Apache Kafka 实战一书的总结,可能有理解错误,仅作为参考。如有侵权,笔者将会删除它们。
注:这篇文章是Kafka文章的第四篇,笔者建议从头看,如果读者感兴趣可以先看第四篇
从架构的角度看Kafka(四)
这篇文章是基于Kafka 10.0.0,如果读者的版本不一致,请查看版本是否支持这些。
一、consumer
1.1 消费者组
1.1.1 消费者组介绍
消费者组是Kafka一大亮点,官方给出的定义翻译说来是:消费者使用一个消费者组名来标记自己,topic的每条消息只会被发送到每个订阅它的消费者组中的一个实例中。从这段话中我们得知了消费者组的三个信息。①一个消费者组可以有多个消费者实例、②一条消息只会被订阅它的消费者组的一个实例消费、③topic消息可以发送到所有订阅它的消费者组。
消费者组使得消费者具有了集群一样的伸缩性、高容错和负载均衡的功能。一个消费者组就像一个集群一样,当一个消费者实例挂掉后,Kafka可以做出响应,将挂掉实例 负责的分区负载到其他实例。这个机制我们会详细的讲解。
当消费者组中有多个实例消费一个topic时,Kafka只能做到分区有序性,不能做到全局有序性。这个也很容易理解,消费者组中的各个实例消费的速度不尽相同,所以必然会导致每个分区的消息消费速度不尽相同。所以如果想要保证消息全局的有序性,必须使消费者组中只有一个实例。
1.1.2 offset
这个offset指的是消费者的offset。每个消费者会维护自己消费的分区的数量。书中探讨了这个offset保存在每个broker中的优缺点,如下。
Kafka将offset交由consumer group来保存,这样消费者组只需要保存一些键值对来表示各个分区的offset。
1.1.3 位移提交
位移提交是很重要的一个必须的功能,他记录了消费者组对topic分区的消费情况,位移的准确性保证了消息的可靠性。书中作者经常会拿新旧版本的消费者客户端进行比较。对于旧的客户端而言他会把我们的分区的offset保存在这zk中,事实上这并不是很合理。zk总是作为一个服务协调者进入我们视野,保存状态并不是他的优势。所以新版本的客户端采用了一个全新的模式。将offset保存在topic中,笔者认为这样对用户更加亲和。下面我们就来介绍一下新版consumer是如何保存offset的。
新版的客户端将offset信息保存在一个名为(__consumer_offsets)的topic中,这个topic是Kafka自动创建的。事实上旧版consumer也是可以使用这个topic的,需要设置consumer的offset.storage=kafka。但是书中认为这并不常见所以没有过多的讲解。在这个topic中,groupId+topic+分区号确定一条offset,这条数据的值就为offset,读者可以想象成他是一个KV,结构为groupId+topic+分区号:offset。
在这个topic创建成功后通常会自动生成50个日志文件夹用于保存offset信息。这样是为了减少一个文件的压力过大,使性能降低。所以Kafka分成了50个文件这样可以同时操作多个文件,从而提高性能。
1.1.4 消费者组重平衡。
在上面介绍消费者组的时候我们说过这个概念了,就是让消费组像一个集群系统一样,具有类似于负载均衡以及故障转移的功能。这也是Kafka消费者组出彩的一部分。
1.1.5使用consumer
使用consumer的方式和我们前几篇文章中的producer的方式基本相同,笔者将讲解几个比较重要的参数。consumer的模板代码如下,consumer还可以设置更多的属性来提高consumer的性能。如果读者有兴趣可以在Kafka官网查看更多的参数。
Properties pro = new Properties();
pro.put("bootstrap.sever","localhost:9092"); //设置Kafka地址
pro.put("groupId","testGroup"); //设置消费组
pro.put("enable.auto.commit","true"); //设置是否自动提交 这个参数后面还会详细讲
pro.put ("auto.commit.interval.ms", "1000") ; //设置自动提交间隔时间
pro.put ("auto.offset.reset", "earliest"); //设置从哪开始消费
pro.put("session.timeout.ms","1000"); //标明consumer检测失败时间。
pro.put ("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
pro.put ("value.deserializer", "org, apache.kafka.common serialization.stringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer(pro) ;
consumer.subscribe (Arrays.asList ("topic-1","topic-2","topic-3")); //订阅topic
//consumer.subscribe(Pattern.compile("topic.*"), new ConsumerRebalanceListenerImp() );
ConsumerRecords<String,String> mes = consumer.poll(Duration.ofSeconds(1)); //消费消息
auto.offset.reset这个参数有三个取值分别为earliest、latest和none。当存在已提交的offset时earliest和latest都会从已提交的offset出进行消费。如果不存在已提交的offset时,earliest会从头开始消费,而latest只会消费新消息。consumer从最早的消息开始消费,如果设置为none时,如果各个分区都存在offset会从offset处开始消费,如果有一个分区不存在offset就会抛异常。
(摘自https://blog.csdn.net/xianpanjia4616/article/details/84347087)
值得注意的是consumer可以同时订阅多个topic,支list和正则表达式。而且订阅并不是增量式的,所以后面再调用这个方法会自动覆盖前面的订阅。无论是正则表达式还是list都支持传入一个org.apache.kafka.clients.consumer.ConsumerRebalanceListener的实现类,这个是用来做rebalance的后面我们会来探讨这个接口。
session.timeout.ms这个参数是一个非常重要的参数。这个参数表示间隔多长时间对消费者组中的实例进行健康检,比如说我们设置了1分钟,当一个实例出现问题时可能1分钟之后才能将这个实例剔除。所以这个时间我们希望它很短,尽快将出现问题的实例进行剔除,以免分区消息滞留。但是在Kafka0.10.1.0之前,这个参数还有一个意义就是我们一个消费者实例两个拉取消息的间隔超过设定时间时,Kafka会认为这个实例出现了问题。更严重的是实例在被剔除后消费消息,offset是无法被提交的,当rebalance后消息会被重复消费一次。所以这个参数设置多少是一个很需要探讨的问题。带来这个问题的是Kafka设计上的缺陷导致的,在Kafka0.10.1.0以后,Kafka修复了这个问题,session.timeout.ms只负责单一的任务,就是标明consumer检测失败时间。
max.poll.interval.ms这个参数就是从上面的参数剥离出来的,代表consumer两次poll的时间超过这个时间后认定为实例出现了问题。这个参数的大小设置需要根据消费消息的时长来决定,如果一个消息处理时间很长,平均时长达到了30s,那么我们可以将这个参数设置的相对大一点,比如1分钟。反之我们需要设置的小一点。
fetch.max.bytes和max.poll.records,第一个参数表达了单次poll拉取消息的最大字节数,如果业务中消息很大,应该超过消息的最大大小,否则将无法消费。第二个参数表达了单次poll拉取消息的最大数量。如果consumer的瓶颈是poll的速度太慢,尝试调大它可以提高你的系统性能。
heartbeat.interval.ms这个参数表面看起来是心跳间隔时间,既然上面已经有了session.timeout.ms参数为什么还需要这个参数呢。事实上两个参数并不相同,当group需要新的一轮rebalance时需要将请求塞进consumer心跳请求的response中,然后实例收到response时进行rebalance。读者可能并不是很理解这句话。实际上现在读者并不需要完全理解,我们后面会将coordinator,那是可能大家就会明白。我们只需要知道他就是我们需要rebalance时到开始的时间限制即可。通常我们希望这个过程很短,所以我们可以设置的小一些。
connection.max.idle.ms Kafka会定期关闭空闲的连接,这个参数标明连接空闲时间超过这个参数时就会被关闭,所以如果读者不在乎socket资源的空闲占用可以设置为-1,这样可以提升consumer性能。
总结
文章中讲了consumer的consumer group、offset、rebalance的原理、设计,消费者构建。可能文字会让人失去读他的欲望,但是还是希望读者可以读完。谢谢观看,如有错误读者可以私信或者评论,笔者秉持宁可造人嘲讽不可误导他人的理念,能力有限,希望大家带着“挑刺”的心态去读文章。