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.后者采用了一种过程化 ...
随机推荐
- echarts 折线图自定义颜色与修改legend颜色
option4 = { title : { text: '', subtext: '' }, color:['#2db7f5','#ff6600','#808bc6'], //关键加上这句话 ...
- vue md5
安装 cnpm install js-md5 --save 使用 import md5 from 'js-md5' md5('要加密的内容')
- Java 集合系列03之 ArrayList详细介绍
ArrayList做为List接口中最常用的实现类,必须掌握. 一.ArrayList简介 与Java中的数组相比ArrayList的容量可以动态增加.它继承与AbstractList,实现了List ...
- CSS3 Background-clip
上一节在<CSS3 background-size>详细的介绍了CSS3为background新增属性之一,今天和大家一起来学习CSS3中有关于Background的第二新属性Backgr ...
- Exp2 后门原理与实践 20164314 郭浏聿
1.实践内容 (1)使用nc实现win,Linux间的后门连接. 热身 (2)使用netcat获取主机操作Shell,cron启动. (3)使用socat获取主机操作Shell, 任务计划启动. (4 ...
- shell 批量远程主机执行命令
[yunwei@Y24-209 ~]$cat ls.sh #!/bin/bash ip55=`cat ip1` for i in $ip55;do ping -c 1 $i if [ $? -eq 0 ...
- java平台学习笔记
java程序从编写源码开始到程序执行一共有三个阶段,编写期,编译期,运行期. 通常,人们都希望自己的程序更快(不仅仅是执行更快,也有编写更快),因此java在不断更新. java源码先通过javac编 ...
- Vagrant上运行SITL
打算给Tower加个手机遥控的功能,用运行于vagrant sitl来联合调试,在公司的网络条件很好的情况下没出任何错,都是根据http://ardupilot.org/dev/docs/settin ...
- 软件测试面试必问--bug交互流程
目前市场主要用的bug管理工具:禅道.jira.QC.bugfree等,当然也有自己公司开发的. 不过不管哪一种工具,核心交互流程都是差不多的,只是字段的名称不一样而已,参考如下两张示意图: 这是前几 ...
- Pytorch学习笔记(二)---- 神经网络搭建
记录如何用Pytorch搭建LeNet-5,大体步骤包括:网络的搭建->前向传播->定义Loss和Optimizer->训练 # -*- coding: utf-8 -*- # Al ...