Rocketmq-尝试理解
普通的信息发送和消费
首先要启动nameserver和broker,nameserver是一个几乎无状态节点。broker分为master和slave,master和slave的对应关系通过指定相同的BrokerName,不同的BorkerId来定义,BrokerId为0表示Master,其他为Slave。每个Broker和Name Server集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。
Producer在发送消息前,获取DefaultMQProducer,指定groupName,设置namesrvAddr。开始启动producer,用本身实例defaultMQProducer生成MQClinetManager的实例mQClinetFactory,向mQClinetFactory注册producer也就是向ConcurrentHashMap类型的producerTable中put。然后mQClientFactory.start()。
获取并设置NameServer Addr。
启动通讯服务。
启动定时任务(1:定时获取NameServerAddr,2:定时更新路由信息(也就是更新DefaultMQProducerImpl中的topicPushlishInfoTable)。3:持久化消费记录,4:动态调整线程池)。
pullMessageService.start()。
负载均衡启动
再次调用DefaultMQProducerImp.start(false)这次不走mQClientFactory.start()。
向所有Broker发送心跳,还有一个方法uploadFilterClassSource()(应该是加载过滤器的)
获取完producer(DefaultMQProducer)后,初始化要发送的消息,消息中设置topic,tag,key,消息体(body)。
发送消息。producer.send()
获取路由信息(从topicPublishInfoTable获取topic的TopicPushlishInfo,没有路由信息或 !topicPublishInfo.ok()从NameServer获取,构建请求头,remoteClinic调nameServer获取,看本地是否需要更新,如果需要,更新到本地,获取过来的TopicRouteData转成TopicPublishInfo(producer用的)跟新到本地,也就是向topicPublishInfoTable中put。转成subscribeInfo(consumer用的)向RebalanceImpl.topicSubscribeInfoTable中put)
获取重试次数(timesTotal),循环这么多次发送消息。获取上一次发送失败的lastBrokerName,第一次发送时,mq为空,如果是轮训,依次轮训topic下的MessageQueue,跳过上次失败的,选取一个mq。如果是顺序消息,就是自己根据算法选择一个MessageQueue列表的中的一个mq。
开始发送。根据上面获取的brokername从MQClientInstance.brokerAddrTable获取brokerAddr,如果为空重复上面的获取路由信息。继续获取。构建SendMessageRequestHeader发送。
=========================================================================================
broker端接受到消息,将消息写入到CommitLog中。
=========================================================================================
Consumer启动前,获取Consumer对象(这里是DefaultMQPushConsumer),指定groupName,设置NamesrvAddr,指定ConsumeFromWhere,指定订阅topic,push的方法指定MessageListener。consumer.start()启动
复制订阅关系。defaultMQPushConsumer.subscription复制到RebalanceImpl.subscriptionInner。初始化rebalanceImpl对象。构建offsetStore消费对象。consumeMessageService.start()启动消费消息服务。mQClientFactory注册这个消费者(也就是MQClientInstance.consumerTable中put)。mQClientFactory.start()和上面producer启动的过程一样。
在rebalanceService.start()中,进行负载均衡。大体是遍历所有的topic,构建PullRequest,经过多步调用,放在PullMessageService的pullRequestQueue队列中,让pullMessageService线程去获取。
pullMessageService.start()这个是在rebalanceService.start()上面一行启动的。而pullMessageService.start()才是正真的消费。在DefaultMQPushConsumerImpl.pullMessage()方法中,构造回调函数PullCallback,对拉取消息结果PullResult做处理,具体是,从PullResult中解码出拉取的消息列表,如果消息的订阅tag不为空且不是classFilter过滤模式,则进行tag过滤,然后把过滤后的消息列表装入PullResult,取出pullResult的nextBeginOffset装入当前的pullRequest的NextOffset中,更新统计数据,异步提交ConsumeRequest进行消息消费,接着提交pullRequest准备做下一次拉取消息的请求。PullMessageProcessor.processRequest()接收到拉消息的请求,做一些简单的判断,如检查Broker权限,确保订阅组存在,检查topic是否存在,然后去messageStore里取消息。详细说明:DefaultMessageStore根据请求的Topic和queueId获取对应的ConsumerQueue,根据传入的queueOffset从consumerQueue里取出目标buffer,然后以20个字节为单位循环从目标buffer里取,取出偏移量offsetPy(占8个字节),消息长度sizePy(占4个字节),过滤标识tagCode(占8个字节),判断如果订阅信息匹配tagCode,则以offsetPy和sizePy从commitLog中以取出消息体buffer,存入GetMessageResult,然后再进行下一次取,最后返回GetMessageResult。取出GetMessageResult的NextBeginoffset,minOffset,maxOffet3个属性,设置到responseHeader中,然后把GetMessageResult打包进response后发送到Consumer端。获取到brokeraddr并获取到结果。回到PullCallBack中的onSuccess方法。更新从哪个broker拉去消息,消息过滤,消息中放入最大最小offset。将消息加入正在处理队列ProcessQueue。提交消息到ConsumeMessageService。ConsumerRequest是ConsumeMessageConcurrentlyService的内部类,里面的run方法就是调用了listenner的consumeMessage方法,也就是开始时自己写的那个消费方法。返回提交结果(ConsumeOrderlyStatus),调用ConsumeMessageOrderlyService.this.processConsumeResult继续处理,如果是success,如果自动提交(非事务方式),调用processQueue.commit(),调用msgTreeMapTemp.lastKey()获取提交的offset,清空msgTreeMapTemp,成功消费。非事务提交是更新内存的offsetTable,让定时任务更新到broker上。
ConsumerRequest是一个内部类,分别在ConsumeMessageConcurrentlyService(并发)和ConsumeMessageOrderlyService(顺序)中是不一样的。
Rocketmq-尝试理解的更多相关文章
- rocketMQ基本理解
消息中间件需要解决哪些问题? Publish/Subscribe 发布订阅是消息中间件的最基本功能,也是相对于传统RPC通信而言. Message Priority 规范中描述的优先级是指在一个消息队 ...
- 尝试理解Linux容器进程与宿主机共享内核到底是什么意思?
背景 近期接触容器技术时,经常看到各类比较容器与虚拟机区别的文章中会提到:容器是共享宿主机的内核,而虚拟机则是拥有自己独立的内核,所以不可能在Linux上用容器运行windows,但是用虚拟机则可以. ...
- 消息队列扫盲(RocketMQ 入门)
消息队列扫盲 消息队列顾名思义就是存放消息的队列,队列我就不解释了,别告诉我你连队列都不知道似啥吧? 所以问题并不是消息队列是什么,而是 消息队列为什么会出现?消息队列能用来干什么?用它来干这些事会带 ...
- [译] 理解PHP内部函数的定义(给PHP开发者的PHP源码-第二部分)
文章来自:http://www.hoohack.me/2016/02/10/understanding-phps-internal-function-definitions-ch 原文:https:/ ...
- 【转】七个例子帮你更好地理解 CPU 缓存
我的大多数读者都知道缓存是一种快速.小型.存储最近已访问的内存的地方.这个描述相当准确,但是深入处理器缓存如何工作的"枯燥"细节,会对尝试理解程序性能有很大帮助. 在这篇博文中,我 ...
- JAVA中的数据结构 - 真正的去理解红黑树
一, 红黑树所处数据结构的位置: 在JDK源码中, 有treeMap和JDK8的HashMap都用到了红黑树去存储 红黑树可以看成B树的一种: 从二叉树看,红黑树是一颗相对平衡的二叉树 二叉树--&g ...
- 《深入理解JAVA虚拟机》笔记1
java程序运行时的内存空间,按照虚拟机规范有下面几项: )程序计数器 指示下条命令执行地址.当然是线程私有,不然线程怎么能并行的起来. 不重要,占内存很小,忽略不计. )方法区 这个名字很让我迷惑. ...
- 理解Golang包导入
Golang使用包(package)这种语法元素来组织源码,所有语法可见性均定义在package这个级别,与Java .python等语言相比,这算不上什么创新,但与C传统的include相比,则是显 ...
- 牛客练习赛16D K进制 数论(待理解QAQ)
正解:数论 解题报告: 行吧那就让我一点点推出来趴QAQ
- RocketMQ入门(简介、特点)
简介: RocketMQ作为一款纯java.分布式.队列模型的开源消息中间件,支持事务消息.顺序消息.批量消息.定时消息.消息回溯等. 发展历程: 1. Metaq(Metamorphosis) 1. ...
随机推荐
- linux主次设备号介绍
1.主设备号与次设备号的功能 在Linux内核中,主设备号标识设备对应的驱动程序,告诉Linux内核使用哪一个驱动程序为该设备(也就是/dev下的设备文件)服务:而次设备号则用来标识具体且唯一的某个设 ...
- Linux sync命令的作用
adb shell sync 写缓存命令——sync 在用reboot命令启动unix系统后,系统提示出错信息,部分应用程序不能正常工作.经仔细检查系统文件,并和初始的正确备份进行比较,发现某些文件确 ...
- (2)redis的基本数据结构是动态数组
redis的基本数据结构是动态数组 一.c语言动态数组 先看下一般的动态数组结构 struct MyData { int nLen; ]; }; 这是个广泛使用的常见技巧,常用来构成缓冲区.比起指针, ...
- fildder学习
http://www.cnblogs.com/strick/p/4570006.html#first
- HTML5/CSS3(PrefixFree.js) 3D文字特效
之前在园子里看到一个HTML5/CSS3的文字特效(这里),觉得挺好玩的所以小小的研究了下,不过发现代码都是针对webkit以及FF的所以IE跪了. Runjs 我将示例中的代码进行了精简,后来发现C ...
- HTML DOM简介
HTML DOM简介 1.当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model),HTML DOM模型被创建为对象的树.如下所示: 2.通过可编程的对象模型,Ja ...
- Linux企业运维高效技巧心得及分享
本博文出自51CTO博主 吴光科 的博客,有任何问题请进入博主页面互动讨论! 博文地址:http://wgkgood.blog.51cto.com/1192594/1641247 随着Linux在企业 ...
- Html basic tag
The <p> tag defines a paragraph. http://www.w3schools.com/tags/tag_p.asp The <td> tag de ...
- Ubuntu中文输入法的添加
做了一个英文环境的Ubuntu14.04LTS,为了写博客方便,添加了中文输入法,在网上搜寻了一堆方法,最后找到个靠谱的. 1 添加fcitx输入法框架.(在此框架下有各种输入法) sudo add- ...
- CUBRID学习笔记 9 创建示例数据库
如果你安装的时候没有装数据库,可以后期装 其实和上文基本差不多. cd ~ mkdir CUBRID_databases cd CUBRID_databases mkdir demodb cd dem ...