高可用
常用的消息队列的高可用是怎么设计的呢?
消息队列一般都有一个 nameserver 服务, 用来检测 broker 是否存活, 或者处理能力上是否存在延迟. 这样在发送消息时就可以规避将消息发送到宕机的 broker 上, 也避免因为网络等原因消息处理失败.
那么针对于以上两种情况, 消息队列如何保证高可用方案的呢?
多副本
每个 topic 可以设置几个 partition, 每个 partition 负责存储一部分数据. kafka 的 broker 集群中, 每台机器存储一些 partition, 存放一部分 topic 数据, 这就实现了 topic 数据分布在一个 broker 集群上.
任何一个分布式系统, 内部都有一套多副本冗余机制, 多副本冗余是任何一个分布式系统具备的基本能力.
kafka 中每个 partition 都有多个副本, 其中一个副本是 leader, 其他副本为 follower,leader 和 follower 分布在不同机器上. leader 对外统一提供写服务, leader 接收到消息后 follower 副本会不停的和 leader 通信, 尝试拉去最新数据, 并持久化到本地磁盘.
说到这里不得不说下 ISR, 也就是保持同步的副本, 表示了和 leader 始终保持同步的 follower 有哪些.
比如 follower 由于 fullgc 造成自己卡顿, 使得无法及时从 leader 拉取数据, 会导致这个 follower 数据比 leader 落后很多.
只要 follower 一直和 leader 保持同步关系, 他们就处于同步关系.
每个 partition 都有一个 ISR, 这个 ISR 一定有 leader, 因为 leader 的数据永远是最新的, 然后就是和 leader 保持同步的 follower, 也会在 ISR 里面.
kafka 中有个 acks 参数. 是在 producer 里面设置的, 也就是客户端设置的.
在向 fafka 集群写数据时, 可以设置这个 acks 参数, 这个参数值有: 0,1,all.
0:
意思是 proucer 在客户端只要把消息发送出去, 不管消息有没有在 partition leader 上落盘就不管了. 就认为消息发送成功了.
1:
意思是 producer 生产的消息要确保 partition leader 写入本地磁盘, 就认为成功了, 而不管 follower 有没有同步这条消息.
当然这个是 kafka 的默认设置.
all:
意思是 partition leader 接收到消息后, 持久化到本地, 还要求 ISR 列表中跟 leader 保持同步的那些 follower 要把消息持久了, 才算写入成功. 一般要求 acks=all 时, 必须 isr 列表里面有两个以上的副本配合使用, 起码每个 leader 有一个 follower 才行.
当 broker 回复客户端消息没有写入成功时, 需要客户端进行消息重发.
重试
消息发送时, 一般存在这样的方法:
- for(; times < timesTotal; times++){
- // send message
- }
这里是 client 发送消息时决定的重试次数, 默认值为 3. 重试可以提高消息发送的成功率.
消息发送
默认的消息发送采用对消息队列进行取模, 确定队列.
其他的方式比如轮训方式等.
Kafka 有两个默认的分配策略:
Range: 该策略会把主题的若干个连续的分区分配给消费者.
RoundRobin: 该策略把主题的所有分区逐个分配给消费者.
消费者
消费者向 kafka 订阅 topic, 并从 topic 上接收消息.
消费者属于消费者组, 一个消费组的消费组订阅的是同一个 topic, 每个消费者接收 topic 一个 partition 的消息.
kafka 默认的规则中, 每个分区只能被同一个消费组里面的一个消费者消费.
1 个消费者接收 4 个分区的消息:
2 个消费者接收 4 个分区的消息:
4 个消费者接收 4 个分区的消息:
5 个消费者接收 4 个分区的消息:
如果消费者群组的消费者超过主题的分区数量, 那么有一部分消费者就会被闲置, 不会接收到任何消息.
两个消费者群组对应一个主题:
当一个消费者被关闭或发生崩溃时, 它就离开群组, 原本由它读取的分区将由群组里的其他消费者来读取. 分区的所有权从一个消费者转移到另一个消费者, 这样的行为被称为再均衡. 在再均衡期间, 消费者无法读取消息, 造成整个群组一小段时间的不可用.
通过上面消费者实例数量变化思考一个问题. 在消费者机器重启过程中, 存在 partition 和消费者重新建立联系的情况, 比如最开始有 4 个消费者, 由于并行重启消费者, 可能存在一段时间消费者数量变为 2 个, 当重启完成后消费者数量有变成了 4 个.
这个过程存在消息可能重复发送到同一个消费者消费的情况, 造成重复消费, 如果是对消息重复敏感的应用场景, 我司自研的消息队列组件会提供一个选项, 消息在分区进行主动积压, 默认积压 30s 等待消费者重启完成, 达到稳定的消费者数量.
消费者通过向被指派为群组协调器的 broker 发送心跳来维持它们和群组的从属关系以及它们对分区的所有权关系. 消费者会在轮训消息或提交偏移量时发送心跳. 如果消费者停止发送心跳的时间足够长, 会话就会过期, 群组协调器认为它已经死亡, 就会触发一次再均衡.
如果一个消费者发生崩溃, 并停止读取消息, 群组协调器会等待几秒钟, 确认它死亡了才会触发再均衡. 所以上面的延迟是由于再平衡期间不可用造成的.
当消费者要加入群组时, 它会向群组协调器发送一个 JoinGroup 请求.
第一个加入群组的消费者将成为 "群主". 群主从协调器那里获得群组的成员列表, 并负责给每一个消费者分配分区.
分配完毕之后, 群主把分配情况列表发送给群组协调器, 协调器再把这些信息发送给所有消费者.
每个消费者只能看到自己的分配情况. 这个过程会在每次再均衡时重复发生.
消息消费
kafka 消费者有自己消费偏移量, 这个偏移量是从 kafka 中读取的量, 和 kafka 提交的偏移量不一样. 消费者一般需要第一次和 rebalance 的时候需要根据提交的偏移量来获取数据, 剩下的时候根据自己本地的偏移量来获取.
当消费者使用了自动提交模式, 当还没有提交的时候, 有消费者加入或者移除, 发送 rebalance, 再次消费时, 消费者根据提交偏移量进行, 可能产生重复消费数据.
选举设计
先说分区 leader 的选举, 就是当 ISR 中的 leader 副本挂了, 再重新选举一个过程.
kafka 中的选举大致可以分为三大类:
控制器选举
分区 leader 选举
消费组相关选举
控制器选举:
kafka 集群中有一个或多个 broker, 其中一个 broker 会被选举为 kafka controller, 负责管理整个集群中所有分区和副本状态. 当检测到某个分区的 leader 副本出现故障, controller 负责为该分区选举新的 leader 副本.
如果检测到某个分区 ISR 集合发生变化时, 控制器负责通知所有的 broker 更新元数据信息.
kafka controller 的实现是依赖于 zk 实现的, 哪个 broker 成功在 zk 的 / controller 临时节点创建成功, 就成为 kafka controller.
分区 leader 选举:
在 topic 下增加分区或者分区下线时, 都需要执行 leader 选举.
基本思路是按照 AR 集合中副本顺序查找第一个存活的副本, 并且这个副本在 ISR 集合中.
消费者相关选举:
消费组协调器需要为消费组内的消费者选择一个消费组 leader, 这个选举算法比较简单.
如果消费组内没有 leader, 那么第一个加入消费组的消费者成为组 leader.
如果由于某种原因 leader 消费者退出消费组, 需要重新选举 leader, 消费者协调器维护一个 map 结构, key 为消费组 id,value 为消费者元信息, 默认选择第一个 key 作为 leader.
更多内容:
来源: https://www.cnblogs.com/xiguain/p/10975575.html