Kafka - 06消费者消费消息解析
一、Kafka消费者读取数据流程
1.1 传统流程
- 消费者发送请求给Kafka服务器
- Kafka服务器在os cache缓存读取数据(缓存没有再去磁盘读取数据)
- 从磁盘读取数据到os cache缓存中
- os cache复制数据到Kafka应用程序中
- Kafka将数据(复制)发送到socket cache中
- socket cache通过网卡传输给消费者
1.2 Kafka零拷贝机制 -- linux sendfile技术
- 消费者发送请求给kafka服务
- Kafka服务去os cache缓存读取数据(缓存没有就去磁盘读取数据)
- 从磁盘读取了数据到os cache缓存中
- os cache直接将数据发送给网卡
- 通过网卡将数据传输给消费者
二、消费者组
2.1 消费者组
- topic 的一个分区只能被消费者组下的一个consumer消费。分区可以被不同的消费者组消费。
- 一个consumer可以消费多个分区,也可以不消费分区。
- 实现广播效果,使用不同的group id 去消费即可。
- 消费者组内的消费者down掉,会自动把分区交给其他消费者。重启后,会再把一些分区重新交给消费者处理。
2.2 消费者代码
public class ConsumerTest { public static void main(String[] args) { String topicName = "ttopic2"; String groupId = "consumerTest"; Properties props = new Properties(); props.put("bootstrap.servers","my-node51:9092,my-node52:9092,my-node53:9092"); props.put("group.id", groupId); 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(topicName)); try { while (true) { ConsumerRecords<String, String> records = consumer.poll(1000); for(ConsumerRecord<String, String> record: records) { System.out.println(record.offset() + "," + record.key() + "," + record.value()); } } } catch (Exception e) { e.printStackTrace(); } } }
[root@my-node52 kafka-2.6.0]# kafka-console-producer.sh --bootstrap-server 192.168.6.51:9092 --topic ttopic2 >123 >1234 --- 输出结果 0,null,123 0,null,1234
2.3 偏移量管理
- 每个 consumer内存里保存对每个topic的每个分区的消费offset,定期提交offset。
- 老版本是写入zookeeper, zookeeper是做分布式协调的,轻量级的元数据存储,不适合高并发读写及存储。
- 新版本提交offset发送给Kafka内部topic: __consumer_offsets。
- 提交 key是group.id + topic + 分区号, value是 当前消费offset的值 + 1。
- 每隔一段时间, Kafka对topic进行合并(compact),每个group.id + topic + 分区号只保留最新数据。
- __consumer_offsets 可能接收高并发的请求,默认分区是50。
偏移量监控工具
- KafkaManager: 修改kafka-run-class.sh 增加 JMX_PORT=9988; 重启Kafka进程
2.4 消费异常感知
- hearbeat.interval.ms
- consumer 心跳时间间隔, 与coordinator 保持心跳,感知consumer是否故障.
- 如果某个consumer故障,通过心跳下发rebalance指令给其他consumer,进行rebalance操作。
- session.timeout.ms
- Kafka 多长时间感知不到一个consumer,就认为故障了。默认是10秒。
- max.poll.interval.ms
- 如果两次poll操作之间超过最大间隔时间,认为consumer处理能力太弱,剔除消费者组。
三、消费者核心参数详解
- fetch.max.bytes
- 获取一条消息最大的字节数。默认是1M。
- Producer 发送一条消息的最大值,默认是10M。
- Broker 存储一条消息接受的最大值,默认是10M。
- max.poll.records
- 一次poll返回消息的最大条数,默认是500。
- connection.max.idle.ms
- consumer 跟 broker 的socket连接, 超过空闲时间,自动回收连接;下次消费重新建立socket。
- 建议设置为-1, 不进行回收。
- enable.auto.commit
- 开启自动提交偏移量
- auto.commit.interval.ms
- 每隔多久提交一次偏移量,默认值是5000。
- auto.offset.reset
- earliest: 当各分区下有已提交的offset时,从提交的offset开始消费;无提交时从头开始消费
- latest: 当各分区下有已提交的offset时,从提交的offset开始消费;无提交时从最新开始消费
- none: 当各分区下有已提交的offset时,从提交的offset开始消费;只要有一个分区无提交则抛出异常
四、Group Coordinator
4.1 消费者rebalance机制
- 消费者如何实现rebalance的? 根据coordinator实现。
- 每个 consumer group 都会选择一个broker作为自己的coordinator,
- 负责监控这个消费者组的各个消费者的心跳,以及判断是否宕机,然后开启rebalance等。
4.2 如何选择coordinator
- 首先对 group id 进行hash, 对 __consumer_offsets 的分区数量取模,默认是50。
- __consumer_offsets的分区数可以通过offsets.topic.num.partitions来设置。
- 找到分区后,分区所在的broker机器就是 coordinator机器。
- consumer group下的所有消费者都往这个分区去提交offset。
4.3 消费方案下发
- 每个 consumer 发送 JoinGroup 请求到 coordinator。
- 然后Coordinator从consumer group中选择一个consumer作为leader,
- 把consumer group信息发送给leader,
- leader负责制定消费方案,通过SyncGroup发给Coordinator
- Coordinator把消费方案下发给各个consumer,
- consumer 从指定的分区的leader broker开始进行socket连接及消费消息
4.4 rebalance 策略
- consumer group 靠 coordinator 实现 rebalance
- 三种rebalance策略: range、 round-robin、sticky
- 假设topic有12个分区,消费者组有三个消费者
- range策略: 按照partition的序号范围,默认策略
- 将同一个topic的分区按照序号排序,然后把消费者按照字母顺序排序。
- 用topic的partition分区数量除以消费者线程的数量决定每个消费者线程消费几个分区。
- 如果除不尽, 前面几个消费者线程会多消费一个分区。
- p0-3 consumer1
- p4-7 consumer2
- p8-11 consumer3
- 如果 consumer1宕机, p0-5 分配给consumer2, p6-11分配给consumer3;原本在consumer2的6和7分区被分配到了consumer3上。
- round-robin策略: 轮询策略
- consumer1: 0,3,6,9
- consumer2: 1,4,7,10
- consumer3: 2,5,8,11
- sticky 策略: 尽可能保证在rebalance时,让原本属于consumer的分区不变动,把多余的分区均匀分配。
- consumer1: 0-3; consumer2: 4-7; consumer3: 8-11。
- consumer1 宕机, consumer2: 4-7, 0,1 consumer3: 8-11, 2,3