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. Django中related_name作用

    相当于我们使用related代替了在通过一个对象查询出多个对象集合时,使用表名_set来获取 我先定义两个模型,一个是作者,一个是作者出版的书籍,算是一对多的类型. class Person(mode ...

  2. Transformer【Attention is all you need】

    前言 Transfomer是一种encoder-decoder模型,在机器翻译领域主要就是通过encoder-decoder即seq2seq,将源语言(x1, x2 ... xn) 通过编码,再解码的 ...

  3. java容器-Map

    Map:基本思想是映射表(维护键-值对),HashMap,TreeMap,LinkedHashMap,ConcurrentHashMap等都是基于Map接口实现的map容器,他们特性不同,表现在效率, ...

  4. thrift使用

    一.什么是thrift Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务.它被当作一个远程过程调用(RPC)框架来使用,是由FaceBook为“大规模跨语言服务开发”而开 ...

  5. 开源顶级持久层框架——mybatis(ibatis)——day02

    mybatis第二天    高级映射 查询缓存 和spring整合          课程复习:         mybatis是什么?         mybatis是一个持久层框架,mybatis ...

  6. ArcGIS——使用符号级别区分重叠的面图层

    1.有这样一个面图层shp文件(区域相重叠),需要做出如下右图的效果: 2.导入shp文件后,右键shp文件,属性->符号系统 3.高级->符号级别 将面积最大的图层的级别设为最小,面积最 ...

  7. 第30月第18天 autolayout代码

    1.上下左右 [tipsLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; { id view1 = tipsLabel; id view2 ...

  8. spring依赖注入之构造函数注入,set方法注入

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...

  9. 产品研发不等待 i.MX6Q全新推出增强版本 官方店铺下单双重优惠

    迅为全新推出PLUS版本的i.MX6Q方案,版本介绍:它是NXP公司全新推出的i.MX6Q增强版新品,显著增强了图形和存储器性能,面向较高图形性能的先进消费电子.汽车和工业多媒体应用的多核平台.

  10. 「JavaScript面向对象编程指南」基础

    DOM标准是独立的(即并不依赖JS)操作结构化文档的方式 BOM实际是个与浏览器有关的对象集合,原来没任何标准可言,H5诞生后才被定义了一些浏览器间通用的对象标准 ES5严格模式"use s ...