重复消费、如何保证顺序性、重试机制等问题
消息是如何发送到broker去的?
从注册中心拉取topic路由信息(每隔30s刷新),拉取到topic以后缓存在本地,使用负载均衡挑选此次写入的broker信息
如果说消息要是发送失败了,此时会如何处理
默认有一次重试机制,返回失败以后会把失败的broker机制暂存,然后选择其他的broker进行发送消息
发送高可用 -》broker故障延迟感知 -》 自动重试机制 -》故障退避机制
发送消息的时候,有哪些比较高阶的特性可以使用,按照key hash
使用字段值的hash值,对queue的数量进行取模,就可以确保,同一个字段值会被分配到同一个queue里面
broker如何实现高并发消息数据写入?
- 写入消息有两种方式:随机写、顺序写
Broker写入CommitLog优化
CommitLog文件名是根据偏移量来的
commitlog写入:磁盘文件顺序写 + 磁盘文件 -》page cache内存映射 = 直接就是顺序写内存
consumer高吞吐高读取的状态下,会一直通过consumeQueue读取commitLog中的数据,当MappedFilePageCache还没有写入到commitLog中的时候,会直接去pageCache中读取,这就会形成内存竞争。此时需要基于内存读写分离。transientStorePoolEnabled机制,瞬时存储池机制。内存级别读写分离模式。
内存一般分为三种,一种jvm heap内存 jvm管理堆内存,一种jvm offheap堆外内存,第三种是page cache 是os管理的。
此时broker写入jvm offheap堆外内存,堆外内存写入到page cache中,这样consumer从pageChache中读取数据就不会出现内存竞争了
ConsumeQueue的读取
consumeQueue是从消息文件中进行读取的,这个消息文件中存储这commitLog中的数据偏移量,每条数据20个字节,每个文件30万行,每个文件5.76MB,通过偏移量可以快速进行二分查找
Broker主从同步
4.5版本之前是单纯的主从同步,从节点不能主动切换到主节点
Broker基于raft协议的主从架构
raft协议针对分布式系统,对多台服务器进行leader选举+主从同步复制+主从切换,定义了这样的一套算法和方法论。
每个follower都会给自己设置一个150ms-300ms之间的一个随机时间,随机的倒计时时间,也就是说有的follower可能会倒计时150ms,有的可能是160ms,有的可能是240ms,大家的时间一般是不一样的
肯定有一个follower倒计时时间是最少的,他是最先完成倒计时的
第一个完成倒计时,塑性过来的那个follower,此时他会把自己的身份转变为candidate,他开始把自己变成一个leader候选人,他会开始想要精选成为一个leader,就需要大家一起来投票,想当然,他当然会投票给他自己了
他需要得到别人的认可,这个很关键,他会发送请求给其他的节点,进行拉票的动作,此时收了一个拉票的请求你,就会把这个票 投给你,如果他要是之前投票给别人了,此时就要拒绝你的拉票
Broker状态机
一般节点数量为奇数,这样有利于投票选举。
正常的流程是节点判断自己是follower-》判断是否收到心跳包,如果没有收到心跳包则开始随机倒计时,倒计时时间到了以后将自己角色改为candidate,自己给自己投票当leader,然后给别的节点发送请求,让他们投票,超过半数投票则选举为leader,选举成功以后把自己角色改为leader,给别的节点发送心跳检测。
在raft协议下只有leader可以对外提供服务,follower是不对外提供服务的,他们仅仅是进行数据复制和同步,leader故障的时候,完成重新的leader选举,继续对外提供服务
broker基于DLedger的数据写入流程
Consumer负载均衡
RebalanceService,平均分配算法、轮询分配算法、一致性hash、配置化、机房分配
ConsumerGroup消费进度:
processQueue在线程池中每个线程消费完以后会把消费进度写入到内存中,然后开启异步线程,将消费进度回传,最终写入磁盘
Consumer重复消费:
分布式锁+幂等性防止重复消费