rocketmq生产者代码分析
rocketmq生产者代码分析
环境安装
参考http://rocketmq.apache.org/docs/quick-start/ ,配置环境变量
export NAMESRV_ADDR=localhost:9876
1.1. 安装监控控制台
clone代码https://github.com/apache/rocketmq-externals/tree/master/rocketmq-console ,编译即可生成rocketmq-console-ng-1.0.0.jar,运行即可,默认依赖NAMESRV_ADDR环境变量配置。
java -jar rocketmq-console-ng-1.0.0.jar
打开URL:http://localhost:8080/
非事务性生产者代码分析
首先fork下rocketmq的代码,地址:https://github.com/apache/rocketmq。找到org.apache.rocketmq.example.quickstart.Producer类。
2.1. 构造DefaultMQProducer
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
构造DefaultMQProducer对象,设置了生产者组名和DefaultMQProducerImpl实例变量。
2.2. 设置NamesrvAddr
producer.setNamesrvAddr("localhost:9876");
namesrvAddr为命名服务器的地址,多个以分号分割。
2.3. 启动生产者实例
producer.start();
内部委派调用defaultMQProducerImpl.start方法,然后内部调用方法。
public void start(final boolean startFactory) throws MQClientException;
第一步:this.checkConfig()
内部逻辑为producerGroup不能为空,不能有特殊字符,最大长度为255,不能为默认生产者组名DEFAULT_PRODUCER。
第二步:设置defaultMQProducer的instanceName为PID
第三步:获取MQClientInstance实例
这里使用单例对象MQClientManager管理MQClientInstance实例。根据传递的defaultMQProducer的配置(IP@PID)作为key获取实例,没有则创建一个,并缓存该对象。MQClientInstance封装了所有的远程通信模块,依赖netty。
public MQClientInstance(ClientConfig clientConfig, int instanceIndex, String clientId, RPCHook rpcHook);
上面构造方法设置了NettyClientConfig,ClientRemotingProcessor,MQClientAPIImpl,PullMessageService(拉取消息线程),RebalanceService(消费者负载分配线程),默认的DefaultMQProducer。
第四步:注册DefaultMQProducerImpl到MQClientInstance的producerTable里
第五步:mQClientFactory.start()
启动MQClientInstance,核心功能都在这里。
this.mQClientAPIImpl.start();
设置Netty客户端的参数,包括一些通道处理器NettyEncoder,NettyDecoder,NettyClientHandler,用于发送和接受请求。
this.startScheduledTask();
内部开启各种定时任务,定时获取NameServer的地址,定时从NameServer更新主题路由信息并计算出逻辑队列设置到每个消费者实例和生产者实例里,定时清理下线的Broker,定时发送心跳到所有的Broker(心跳数据包括客户端ID,消费者订阅数据,生产者组信息),定时持久化所有的消费队列的消费进度到Broker。
this.pullMessageService.start();
启动拉取消息线程,不停的从拉取请求队列获取PullRequest,进行指定队列的拉取,拉取采用异步请求(回掉类为PullCallback),默认一次拉取32条,拉取成功的话会按设置的消费模式(并发,顺序),回掉到我们设置的消费类处理。
this.rebalanceService.start();
启动负载均衡线程,默认20秒运行一次,获取订阅主题的所有逻辑队列,并且随机从一个Broker上(前面有每个客户端向所有Broker发送心跳)获取当前消费者组下所有的消费者,默认用AllocateMessageQueueAveragely平均分配策略,分配完会生成PullRequest设置到PullMessageService的请求队列里,以供消息拉取,其中PullRequest会设置上一次的拉取偏移量nextOffset,nextOffset首次从Broker上获取。
this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
启动内部的DefaultMQProducerImpl。
第六步:this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
目的维持tcp长连接,并且注册客户端信息到Broker,包括订阅信息,用于拉取消息的时候Broker根据tag设置过滤。
2.4. 发送消息
Message msg = new Message("TopicTest" /* Topic */,
"TagA" /* Tag */,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
SendResult sendResult = producer.send(msg);
内部委派调用defaultMQProducerImpl.send(msg),最终方法为
private SendResult sendDefaultImpl(
Message msg,
final CommunicationMode communicationMode,
final SendCallback sendCallback,
final long timeout
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException
发送消息默认同步发送,超时时间3秒。
第一步:Validators.checkMessage(msg, this.defaultMQProducer);
发送消息校验,消息topic和内容的大小校验。
第二步:TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
获取主题的发布队列信息,默认为轮询发送。
第三步:MessageQueue tmpmq = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
同步发送方式默认2次重试机会,先根据上一次的Broker选择下一个发送队列
第四步:sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout);
发送消息内核方法,首先获取要发送队列对应Broker的地址,设置消息的UNIQ_KEY属性(规则为PID+IP+时间戳+计数器),如果消息大于4K字节,则开启压缩。设置SendMessageRequestHeader消息头,包括生产者组,主题,队列ID,消息生成时间,消息属性等。生成请求头,请求code,设置到RemotingCommand请求对象里,然后同步发送,超时时间为3秒,并获取相应结果SendResult。发送的时候这里可能会失败,通过DefaultMQProducer.retryAnotherBrokerWhenNotStoreOK参数控制是否发送Broker获取响应结果为失败后重试,默认为false。
2.5. 关闭生产者实例
producer.shutdown();
内部委派调用到DefaultMQProducerImpl的shutdown方法
public void shutdown(final boolean shutdownFactory);
第一步:this.mQClientFactory.unregisterProducer(this.defaultMQProducer.getProducerGroup());
从MQClientInstance卸载当前生产者组,发送请求到所有Broker取消注册信息(之前发送心跳的时候就注册了客户端的信息,包括生产者和消费者)
第二步:this.mQClientFactory.shutdown();
尝试关闭MQClientInstance,当MQClientInstance没有注册的消费者和生产者的时候(MQClientInstance是IP@PID作为key公用的实例),执行关闭步骤,包括pullMessageService(拉取消息),scheduledExecutorService(调度任务使用),mQClientAPIImpl(远程通信模块),rebalanceService(消费者队列负载均衡),最后从MQClientManager单例移除MQClientInstance。
事务性生产者代码分析
找到org.apache.rocketmq.example.transaction.TransactionProducer类。
3.1. 构造TransactionMQProducer
TransactionMQProducer producer = new TransactionMQProducer("please_rename_unique_group_name");
producer.setCheckThreadPoolMinSize(2);
producer.setCheckThreadPoolMaxSize(2);
producer.setCheckRequestHoldMax(2000);
producer.setTransactionCheckListener(transactionCheckListener);
与非事务性不同的是,事务性生产者会有一个TransactionCheckListener(事务消息超时确认回查类),启动方式类似,只是会另外启动一个checkExecutor回查线程池。
3.2. 发送消息
未完待续。。。
rocketmq生产者代码分析的更多相关文章
- RocketMQ生产者消息篇
系列文章 RocketMQ入门篇 RocketMQ生产者流程篇 RocketMQ生产者消息篇 前言 上文RocketMQ生产者流程篇中详细介绍了生产者发送消息的流程,本文将重点介绍发送消息的通信模式以 ...
- RocketMQ源码分析之从官方示例窥探:RocketMQ事务消息实现基本思想
摘要: RocketMQ源码分析之从官方示例窥探RocketMQ事务消息实现基本思想. 在阅读本文前,若您对RocketMQ技术感兴趣,请加入RocketMQ技术交流群 RocketMQ4.3.0版本 ...
- rocketmq源码分析1-benchmark学习
benchmark 分析 组成部分 三个java类,都含有main方法,可选的传递一些参数,诸如测试线程数量,消息体积大小.三个类分别用于测试普通生产者,事务生产者,消费者.生产者 默认64个测试线程 ...
- 【RocketMQ源码分析】深入消息存储(3)
前文回顾 CommitLog篇 --[RocketMQ源码分析]深入消息存储(1) ConsumeQueue篇 --[RocketMQ源码分析]深入消息存储(2) 前面两篇已经说过了消息如何存储到Co ...
- Android代码分析工具lint学习
1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...
- pmd静态代码分析
在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...
- [Asp.net 5] DependencyInjection项目代码分析-目录
微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...
- [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)
Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...
- 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)
构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...
随机推荐
- va_start
#include <stdarg.h> void va_start(va_list ap, last); type va_arg(va_list ap, type); void va_en ...
- C#调用Java的WebService添加SOAPHeader验证(2)
C#调用Java的WebService添加SOAPHeader验证 上一篇链接如上,更像是 Net下采用GET/POST/SOAP方式动态调用WebService的简易灵活方法(C#) 来处理xml, ...
- 浅谈flex布局中小技巧
最近有个面试,面试官问到,在一个横向布局上,假设有三个div,每个宽度为定宽apx,如果想使两侧宽度为x,中间div间间隔为2x.x可以自适应.如下图: 怎么做很简单,两行代码就搞定: justi ...
- Python正则表达式指南(转)
目录 Python正则表达式指南(转) 0.防走丢 1. 正则表达式基础 1.1. 简单介绍 1.2. 数量词的贪婪模式与非贪婪模式 1.3. 反斜杠的困扰 1.4. 匹配模式 2. re模块 2.1 ...
- 浅议极大似然估计(MLE)背后的思想原理
1. 概率思想与归纳思想 0x1:归纳推理思想 所谓归纳推理思想,即是由某类事物的部分对象具有某些特征,推出该类事物的全部对象都具有这些特征的推理.抽象地来说,由个别事实概括出一般结论的推理称为归纳推 ...
- 07--STL序列容器(Array)
一:Array了解 array<T,N> 模板定义了一种相当于标准数组的容器类型.它是一个有 N 个 T 类型元素的固定序列.除了需要指定元素的类型和个数之外,它和常规数组没有太大的差别. ...
- Javascript实现base64的加密解密【转】
场景 这几天使用PHP向前端传值的时候,遇到一个问题,要将代码传过去赋值.如果使用urlencode()和urldecode()函数,就会出现js无法解码的情况,因为php和js的相关函数算法不一致. ...
- numpy&pandas补充常用示例
Numpy [数组切片] In [115]: a = np.arange(12).reshape((3,4)) In [116]: a Out[116]: array([[ 0, 1, 2, 3], ...
- [转载]Yacc 与 Lex 快速入门
https://www.ibm.com/developerworks/cn/linux/sdk/lex/index.html
- mesbox公告加更新控制
0为不显示,1为显示~~~~0|友情提示:任何时候,不要相信福利软件,福利网站,不乱接收别人发的的任何文件,如需使用军旗有关产品,请至官方群或官方网站下载!!|183|173~~~~162,1651, ...