消费分组
消费分组是用来组织消费者、分区、消费进度关系的逻辑概念。消费分组和 Topic 是强相关的。因为 Topic 不存储真实数据,分区才存储消息数据,所以就需要解决消费者和分区的分配关系,即哪个分区被哪个消费者消费,这个分配的过程就叫做消费重平衡(Rebalance)。
协调者
协调者主要的工作就是执行消费重平衡,并记录消费分组的消费进度。
分区分配的操作可以在协调者内部或者消费者上完成。
- 在协调者完成,即协调者首先获取消费者和分区的信息,然后在协调者内部完成分区分配,最后再把分配关系同步给所有消费者。
- 在消费者完成,即负责分配的消费者获取所有消费者和分区的信息,然后该消费者完成分区分配操作,最后再把分配关系同步给其他消费者。
一般在创建消费分组和消费者 / Topic 分区发生变化的时候,会触发协调者执行消费重平衡。
消费分区分配策略
所有消息队列的默认策略都是相对通用的,一般都会包含有轮询、粘性、自定义三种类型的策略。
轮询就是指用轮询的方式将分区分配给各个消费者,保证每个消费者的分区数量是尽量相同的,从而保证消费者的负载最大概率上是均衡的。
粘性是指尽量减少分区分配关系的变动,进而减少重平衡所耗费的时间和资源损耗。即当已经分配好消费者和分区的消费关系后,当消费者或者分区出现变动,就会触发重平衡。从底层来看,可能就是一个消费者掉了或者新增分区。此时需要重新进行分配的消费者和分区其实是有限的,大部分的分配关系可以不动。而此时如果使用轮询算法,则要全部打散重来,耗时就会很长,并且浪费资源,即把原先不需要重新分配的关系都重新分配一遍。
一致性哈希的算法经常用在负载均衡中。用一致性哈希实现粘性策略的优点是,当节点或者分区变动时,只需要执行少量的分区再分配即可。
自定义分区分配算法,内核会提供接口,用户可以根据自身需求实现自定义算法,然后指定配置生效即可。比如 Kafka 提供了 org.apache.kafka.clients.consumer.internals.PartitionAssignor 接口来提供自定义分区分配策略。
消费确认
消息确认分为确认后删除数据和确认后保存消费进度数据两种形式。
确认后删除数据是指集群的每条消息只能被消费一次,只要数据被消费成功,就会回调服务端的 ACK 接口,服务端就会执行数据删除操作。在实际开发的过程中,一般都会支持单条 ACK 和批量 ACK 两种操作。这种方式不利于回溯消费,所以用得比较少。
消费成功保存消费进度是指当消费数据成功后,调用服务端的消费进度接口来保存消费进度。这种方式一般都是配合消费分组一起用的,服务端从消费分组维度来保存进度数据。
在提交位点信息的时候,底层一般支持自动提交和手动提交两种实现。
建议你使用手动提交方式,可以避免数据丢失。
消费失败处理
从服务端拉取数据失败,和客户端的错误逻辑处理是一致的,根据可重试错误和不可重试错误的分类,进行重复消费或者向上抛错。
本地业务数据处理失败,处理起来就比较复杂了。如果是偶尔失败,那么在业务层做好重试处理逻辑,配合手动提交消费进度的操作即可解决。如果是一直失败,即使重试多次也无法被解决,比如这条数据内容有异常,导致无法被处理。此时如果一直重试,就会出现消费卡住的情况,这就需要配合死信队列的功能,将无法被处理的数据投递到死信队列中,从而保存异常数据并保证消费进度不阻塞。
提交位点信息失败,其处理方法通常是一直重试,重复提交,如果持续失败就向上抛错。因为如果提交进度失败,即使再从服务端拉取数据,还是会拉到同一批数据,出现重复消费的问题。
此文章为11月Day9学习笔记,内容来源于极客时间《深入拆解消息队列 47 讲》