【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消息消费
消息消费以组的的模式开展: 一个消费组内可以包含多个消费者,每一个消费组可订阅多个主题: 消费组之间有集群模式与广播模式两种消费模式:集群模式-主题下的同一条消息只允许被其中一个消费者消费.广播模式- ...
随机推荐
- java log4j 的一个bug
java项目中使用log4j记录日志几乎成了标配, 最近一个项目中出了个问题 现象是这样的: 不连vpn程序一切正常,连上VPN启动程序 直接异常退出, 错误日志直接指向了 log4j 库 org ...
- C语言实例-能组成多少个三位数?
题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 程序分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去 掉不满足条件的排列. 源代码: #i ...
- LoRa技术的发展应用和LoRa应用设备
LoRa技术的发展应用 LORA技术大约在十年前由法国和瑞士开发,到现今LORA技术已经是物联网发展应用中不可缺少的一部分,根据中国物联网研究与发展中心的数据,2025年我国物联网产业规模将达到2万亿 ...
- 【Android Studio】安卓开发初体验1——安装与试用
安装 (安装预留硬盘(C盘)空余空间16G以上,8G以上内存) Intel用户: 安装Android Studio和AVD(安卓虚拟机 Android Virtual Device) 安装过程需要注意 ...
- abp(net core)+easyui+efcore实现仓储管理系统——出库管理之五(五十四)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...
- 模拟微信小程序页面Page方法
1.依赖 a.jQuery b.angularjs 2.page.js文件 1 var Page = function (options) { 2 var myApp = angular.module ...
- Pytest学习(七) - skip、skipif的使用
前言 作为一个java党,我还是觉得pytest和testng很像,有时候真的会感觉到代码语言在某种程度上是相通的,那么今天来说说这两个知识点. skip和skipif,见名知意,就是跳过测试呗,直白 ...
- Electron入门指北
最近几年最火的桌面化技术,无疑是Qt+和Electron. 两者都有跨平台桌面化技术,并不局限于Windows系统.前者因嵌入式而诞生,在演变过程中,逐步完善了生态以及工具链.后者则是依托于Node. ...
- Windows平台Python Pyramid实战从入门到进阶:第一个服务
Pyramid是比较流行的Python Web 框架,比较灵活,功能也很强大.最近项目上用到,便打算学习一下.网上教程比较少,而且很多都是针对linux平台的,我是windows土著所以对那些linu ...
- 3 jinja2模板
video17 jinja2过滤器 过滤器通过管道符号进行使用.如{{ name | length }}将返回name的长度,过滤器相当于是一个函数. 1 def hello_world(): 2 i ...