【mq读书笔记】mq读写分离机制
mq根据brokerName查找Broker地址的过程
mq根据MessageQueue查找Broker地址的唯一依据是brokerName,同一组Broker(M-S)他们的bokerName相同但brokerId不同,主服务器的brokerId为0,从服务器的brokerId大于0,rokckertMq提供mQClientFactory.findBrokerAddressInSubscribe来实现根据brokerName,brokerId查找Broker地址
org.apache.rocketmq.client.impl.consumer.PullAPIWrapper#pullKernelImpl:
public PullResult pullKernelImpl(
final MessageQueue mq,
final String subExpression,
final String expressionType,
final long subVersion,
final long offset,
final int maxNums,
final int sysFlag,
final long commitOffset,
final long brokerSuspendMaxTimeMillis,
final long timeoutMillis,
final CommunicationMode communicationMode,
final PullCallback pullCallback
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
FindBrokerResult findBrokerResult =
this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(),
this.recalculatePullFromWhichNode(mq), false);
FindBrokerResult:
public class FindBrokerResult {
private final String brokerAddr;
private final boolean slave;
private final int brokerVersion;
org.apache.rocketmq.client.impl.factory.MQClientInstance#findBrokerAddressInSubscribe:
public FindBrokerResult findBrokerAddressInSubscribe(
final String brokerName,
final long brokerId,
final boolean onlyThisBroker
) {
String brokerAddr = null;
boolean slave = false;
boolean found = false;
//获取brokerName下的所有broker
HashMap<Long/* brokerId */, String/* address */> map = this.brokerAddrTable.get(brokerName);
if (map != null && !map.isEmpty()) {
//根据brokerId获取broker地址
brokerAddr = map.get(brokerId);
slave = brokerId != MixAll.MASTER_ID;
found = brokerAddr != null; if (!found && !onlyThisBroker) {
Entry<Long, String> entry = map.entrySet().iterator().next();
brokerAddr = entry.getValue();
slave = entry.getKey() != MixAll.MASTER_ID;
found = true;
}
} if (found) {
return new FindBrokerResult(brokerAddr, slave, findBrokerVersion(brokerName, brokerAddr));
} return null;
}
从ConcurrentMap<String/* Broker Name */, HashMap<Long/* brokerId */, String/* address */>> brokerAddrTable地址缓存表中根据brokerName获取所有的Broker信息。
根据brokerId从Broker主从缓存表中获取指定Broker名称,日过根据brokerId未找到相关条目,此时onlyThisBroker未false,则随机返回Broker中任意一个Broker,否则返回null。
根据消息消费队列获取brokerId的实现:
public long recalculatePullFromWhichNode(final MessageQueue mq) {
if (this.isConnectBrokerByUser()) {
return this.defaultBrokerId;
}
//pullFromWhichNodeTable是在哪初始化的?
AtomicLong suggest = this.pullFromWhichNodeTable.get(mq);
if (suggest != null) {
return suggest.get();
}
return MixAll.MASTER_ID;
}
从ConcurrentMap<MessageQueue, AtomicLong/* brokerId */> pullFromWhichNodeTable缓存表中获取该消息消费队列的brokerId,如果找到则返回,否则返回brokerName的主节点。
消息消费拉取线程PullMessageService根据PullRequest请求从主服务器拉取消息后会返回下一次建议拉取的brokerId,消息消费者线程在收到消息后,会根据主服务器的建议拉取brokerId来更新
pullFromWhichNodeTable。


org.apache.rocketmq.client.impl.consumer.PullAPIWrapper#updatePullFromWhichNode:
public void updatePullFromWhichNode(final MessageQueue mq, final long brokerId) {
AtomicLong suggest = this.pullFromWhichNodeTable.get(mq);
if (null == suggest) {
this.pullFromWhichNodeTable.put(mq, new AtomicLong(brokerId));
} else {
suggest.set(brokerId);
}
}
消息服务端是根据何种规则来建议哪个消息消费队列该从哪台Broker服务器上拉取消息呢?
org.apache.rocketmq.store.DefaultMessageStore#getMessage:
long diff = maxOffsetPy - maxPhyOffsetPulling;
long memory = (long) (StoreUtil.TOTAL_PHYSICAL_MEMORY_SIZE
* (this.messageStoreConfig.getAccessMessageInMemoryMaxRatio() / 100.0));
getResult.setSuggestPullingFromSlave(diff > memory);
maxoffsetPy:主服务器消息存储文件最大偏移量
maxPhyPffsetPulling:此次拉取消息最大偏移量
diff:对于PullMessageService线程来说,当前未被拉取到消息消费端的消息长度。
TOTAL_PHYSICAL_MEMORY_SIZE:
mq所在服务器总内存大小。accessMessageInMemoryMaxRatio表示RocketMQ所能使用的最大内存比例,超过该内存,消息江北置换出内存;
memory表示RocketMQ消息常驻内存的大小,超过该大小,rocketMq将会将旧的消息置换回磁盘。
如果diff大于memory,表示当前需要拉去的消息已经超出了常驻内存的大小,表示主服务器繁忙,此时才建议从从服务器拉取。
org.apache.rocketmq.broker.processor.PullMessageProcessor#processRequest(Channel, org.apache.rocketmq.remoting.protocol.RemotingCommand, boolean):
if (getMessageResult != null) {
response.setRemark(getMessageResult.getStatus().name());
responseHeader.setNextBeginOffset(getMessageResult.getNextBeginOffset());
responseHeader.setMinOffset(getMessageResult.getMinOffset());
responseHeader.setMaxOffset(getMessageResult.getMaxOffset());
if (getMessageResult.isSuggestPullingFromSlave()) {
responseHeader.setSuggestWhichBrokerId(subscriptionGroupConfig.getWhichBrokerWhenConsumeSlowly());
} else {
responseHeader.setSuggestWhichBrokerId(MixAll.MASTER_ID);
}
如果一个Master拥有多台Slave服务器,参与消息卡去负载的从服务器只会是其中一个。
【mq读书笔记】mq读写分离机制的更多相关文章
- 【mq读书笔记】消息过滤机制
mq支持表达式过滤和类过滤两种模式,其中表达式又分为TAG和SQL92.类过滤模式允许提交一个过滤类到FilterServer,消息消费者从FilterServer拉取消息,消息经过FilterSer ...
- 基于Keepalived高可用集群的MariaDB读写分离机制实现
一 MariaDB读写分离机制 在实现读写分离机制之前先理解一下三种主从复制方式:1.异步复制:MariaDB默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库 ...
- 【mq读书笔记】消息队列负载与重新分配(分配 新队列pullRequest入队)
回顾PullMessageService#run: 如果队列总没有PullRequest对象,线程将阻塞. 围绕PullRequest有2个问题: 1.PullRequest对象在什么时候创建并加入p ...
- 使用Amoeba实现mysql读写分离机制
Amoeba的实用指南 http://docs.hexnova.com/amoeba/ 如何实现mysql读写分离 : 通常来说有两种方式: 1,应用程序层实现 2,中间件层实现 应用层实现 应用层实 ...
- 【mq读书笔记】消息拉取长轮训机制(Broker端)
RocketMQ并没有真正实现推模式,而是消费者主动想消息服务器拉取消息,推模式是循环向消息服务端发送消息拉取请求. 如果消息消费者向RocketMQ发送消息拉取时,消息未到达消费队列: 如果不启用长 ...
- 【mq读书笔记】消息确认(失败消息,定时队列重新消费)
接上文的集群模式,监听器返回RECONSUME_LATER,需要将将这些消息发送给Broker延迟消息.如果发送ack消息失败,将延迟5s后提交线程池进行消费. 入口:ConsumeMessageCo ...
- 【mq读书笔记】消息消费过程(钩子 失败重试 消费偏移记录)
在https://www.cnblogs.com/lccsblog/p/12249265.html中,PullMessageService负责对消息队列进行消息拉取,从远端服务器拉取消息后将消息存入P ...
- 【mq读书笔记】消息拉取
疑问:PullRequest何时添加? PullMessageService提供延迟添加与立即添加2种方式 疑问:PullRequest是在什么时候创建的呢? 1.上上图中 PullRequest p ...
- 【mq读书笔记】mq消息消费
消息消费以组的的模式开展: 一个消费组内可以包含多个消费者,每一个消费组可订阅多个主题: 消费组之间有集群模式与广播模式两种消费模式:集群模式-主题下的同一条消息只允许被其中一个消费者消费.广播模式- ...
随机推荐
- SP22343 Norma--序列分治
Norma 传送门 题意简化: 定义一个区间的贡献为 \(max*min*len\),求给定序列中所有子区间的总贡献和 题解 考虑 \(O(n*log_2n)\) 的复杂度的做法 数据结构??? yz ...
- K8S的StorageClass实战(NFS)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 4g工业路由器的覆盖范围分析
4G工业路由器通常覆盖范围在60-80米之间,而影响4G工业路由器的覆盖范围有以下几个因素. 影响4g工业路由器覆盖范围的因素一:环境 空旷的环境下4g工业路由器的信号覆盖范围必然更加广阔,如果传输环 ...
- PASS模型-第一周个人总结
目录 PASS模型-第一周个人报告 1.个人任务 2.个人工作内容 2.1 登陆界面 2.2 信息采集 2.3 视觉搜索 3.个人小结 3.1 收获 3.2 优化 PASS模型-第一周个人报告 博客班 ...
- (C#2,.net framework2.0,Visual Studio 2003)之前版本
(C#2,.net framework2.0,Visual Studio 2003)之前版本归为最初的版本(主要是针对.net framework),其主要定义了最基本的类型.特性. 1.基本的类型 ...
- Docker系列02—Docker 网络模式
一.Docker的四种网络模式 1.Docker 的四种网络模式: Bridge container 桥接式网络模式 Host(open) container 开放式网络模式 Container(jo ...
- 深入浅出 webpack 之基础配置篇
前言 前端工程化经历过很多优秀的工具,例如 Grunt.Gulp.webpack.rollup 等等,每种工具都有自己适用的场景,而现今应用最为广泛的当属 webpack 打包了,因此学习好 webp ...
- ceph与flashcache的around模式结合启动问题
问题 通过对我们的启动流程看了下,目前是穿到一个脚本里面的,然后这个脚本是用无限循环的方式去执行一些事情,这个地方不符合松耦合的设计,一个模块做一个事情,两个并不相关的功能不要嵌入另一个脚本,否则出现 ...
- if __name__ == "__main__"的疑惑
Python中if __name__ == "__main__"详细解释: 想必很多初次接触python都会见到这样一个语句,if __name__ == "__main ...
- python实现祝福弹窗
接下来教大家怎么用python献上满满的祝福.首先给大家看一下最终模样. 注:密集恐惧症患者勿入! 模样大家已经看见了,首先想想一下,当朋友打开你发送过去的软件, 然后点击运行,结果,一个一个的窗口慢 ...