rocketmq生产者代码分析

  1. 环境安装

    参考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/

  2. 非事务性生产者代码分析

    首先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。

  3. 事务性生产者代码分析

    找到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生产者代码分析的更多相关文章

  1. RocketMQ生产者消息篇

    系列文章 RocketMQ入门篇 RocketMQ生产者流程篇 RocketMQ生产者消息篇 前言 上文RocketMQ生产者流程篇中详细介绍了生产者发送消息的流程,本文将重点介绍发送消息的通信模式以 ...

  2. RocketMQ源码分析之从官方示例窥探:RocketMQ事务消息实现基本思想

    摘要: RocketMQ源码分析之从官方示例窥探RocketMQ事务消息实现基本思想. 在阅读本文前,若您对RocketMQ技术感兴趣,请加入RocketMQ技术交流群 RocketMQ4.3.0版本 ...

  3. rocketmq源码分析1-benchmark学习

    benchmark 分析 组成部分 三个java类,都含有main方法,可选的传递一些参数,诸如测试线程数量,消息体积大小.三个类分别用于测试普通生产者,事务生产者,消费者.生产者 默认64个测试线程 ...

  4. 【RocketMQ源码分析】深入消息存储(3)

    前文回顾 CommitLog篇 --[RocketMQ源码分析]深入消息存储(1) ConsumeQueue篇 --[RocketMQ源码分析]深入消息存储(2) 前面两篇已经说过了消息如何存储到Co ...

  5. Android代码分析工具lint学习

    1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...

  6. pmd静态代码分析

    在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...

  7. [Asp.net 5] DependencyInjection项目代码分析-目录

    微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...

  8. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)

    Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...

  9. 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)

    构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...

随机推荐

  1. [转载]如何在ubuntu上使用github

    来源:https://blog.csdn.net/tina_ttl/article/details/51326684 https://blog.csdn.net/u013551462/article/ ...

  2. ES6.3.2 index操作源码流程

    ES 6.3.2 index 操作源码流程 client 发送请求 TransportBulkAction#doExecute(Task,BulkRequest,listener) 解析请求,是否要自 ...

  3. CentOS Linux change IP Address

    1.change network card configure edit: vi /etc/sysconfig/network-scripts/ifcfg-eth0 ps:notice HWADDR! ...

  4. mysql的The user specified as a definer (”@’%') does not exist 的解决办法

    两种可能: 1.用户权限不够 赋给用户所有权限试试 mysql> grant all privileges on *.* to root@"%" identified by ...

  5. TERADATA SQL学习随笔<一>

    此博客内容简介及目录 http://www.cnblogs.com/weibaar/p/6644261.html 最近在TERADATA环境学习SQL.在这里记录一下学习中查过的知识点,作为备案. 目 ...

  6. 模块--random

    random模块 1       random.random() print(random.random()) 0-1 之间随机小数 不包含1 2       random.uniform(a,b) ...

  7. 论文翻译——Lattice indexing for spoken term detection

    第II节简要介绍与本文有关的先前工作第III节介绍文中使用的定义以及术语 第IV节介绍如何从原始ASR lattices中生成倒排索引结构 第V节详细介绍了ASR结构以及实验使用的数据 第VI节提供了 ...

  8. java 中final关键字

    1.final变量,一旦该变量被设定,就不可以再改变该变量的值. final关键字定义的变量必须声明时赋值.一旦一个对象引用被修饰为final后,它只能恒定指向一个对象,一个既是static和fina ...

  9. 用CSS实现加载的动画效果

    用纯CSS实现加载的一些动态效果,可以把加载效果中的元素分成很多个小部分,每个部分都有动画,每个部分的动画再设置相应的延迟效果,这样,看起来就是连贯的加载动画效果.代码如下: 1.效果1 <di ...

  10. KML,SHP TAB互转,GDAL

    最近在接触地图数据转换的东西,从硬件kml的数据转换其他的格式,因为从没做过着东西, 先去了解kml文件格式 http://baike.baidu.com/view/400307.htm?fr=ala ...