rocketmq 源码分析 运行 亲测 附图附代码 2018.12

本文详细介绍了如何从源码编译并部署Apache RocketMQ,包括配置环境变量、启动NameServer和Broker集群,以及使用Docker简化部署过程。此外,还提供了生产者和消费者的Java代码示例,展示了如何通过NameServer进行消息的发布与订阅。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1

.rocketmq 源码下载

git clone https://github.com/apache/rocketmq.git

 

2. 运行namesrv启动方法  默认端口9876

# run
org.apache.rocketmq.namesrv.NamesrvStartup#main

# 会报错
Please set the ROCKETMQ_HOME variable in your environment to match the location of the RocketMQ installation

# 根据提示 配置ROCKETMQ_HOME 环境变量  工程根目录下找到/rocketmq/distribution 取这个绝对路径
ROCKETMQ_HOME=/Users/IdeaProjects/rocketmq/distribution  如下图

再次启动  OK

3.运行broker 启动方法

# 运行
org.apache.rocketmq.broker.BrokerStartup#main

# 报错 ROCKETMQ_HOME 需要配置环境变量 如上

# 同时 需要告诉broker Namesrv的地址 通过环境变量配置
NAMESRV_ADDR=127.0.0.1:9876

再次启动 ok   

 

 

抱歉以下未整理 因为图片大小难以控制 仅给自己留个痕

界面工具已经连接到了namesrv

broker 已经连接到了namesrv

新建一个topic

 

我们上面选的1  这里只出现一个队列  如你设置5 就会有5个队列 这些队列会平分收到的消息 (生产者发送100条,则每个队列20条)

 

生产者启动后在这里可以查看到 消费者启动后也可以在这里看到

在这里可以看到生产者发的所有消息

 

 

 

 

 

消费者启动后不会停止 会一直监听消息队列, 一有新消息产生, 消费者就一直在打印

 

 

 

 

 

1.NameServer集群

     提供轻量级的服务发现和路由。 每个 NameServer 记录完整的路由信息,提供等效的读写服务,并支持快速存储扩展

2.Broker集群

     每个Broker启动时都会将自己注册到NameServer上,其通过提供轻量级的 Topic 和 Queue 机制来处理消息存储,同时支持推(push)和拉(pull)模式以及主从结构的容错机制,Broker Master1 和 Broker Slave1 是主从结构,它们之间会进行数据同步,即 Date Sync。一个Master1可以对应多个Slave1,但是一个Slave1只能对应一个Master1。

3.Producer集群

     产生消息的实例,拥有相同 Producer Group 的 Producer 组成一个集群。

4.Consumer集群

    接收消息进行消费的实例,拥有相同 Consumer Group 的Consumer 组成一个集群

 

Producer 与 NameServer 集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Broker Master 建立长连接,且定时向 Broker 发送心跳。Producer 只能将消息发送到 Broker master。

Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。

 

 

组成

https://github.com/apache/rocketmq-externals/tree/master/rocketmq-docker 

在官方git下查看安装文档,这里我们选择使用docker方式.

(如果你不想使用docker,那就不要继续往下看了 右侧  从源码角度启动rocketmq)。

$ git clone https://github.com/apache/rocketmq-externals.git
......
Resolving deltas: 100% (4143/4143), done.
$ cd rocketmq-externals/rocketmq-docker/4.3.0
$ sh play-docker.sh
Sending build context to Docker daemon   12.8kB
Step 1/10 : FROM centos:7
......
$ docker images
REPOSITORY                      TAG                 IMAGE ID            CREATED             SIZE
apache/rocketmq-broker          4.3.0               0741f6251b82        20 seconds ago      380MB
apache/rocketmq-namesrv         4.3.0               2cf4e87c274c        22 seconds ago      380MB
apache/rocketmq-base            4.3.0               a161d2692b67        25 seconds ago      380MB
$ docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                                NAMES
7cba766fa7b3        apache/rocketmq-broker:4.3.0    "/bin/sh -c 'cd ${RO鈥?"   6 minutes ago       Up 6 minutes        0.0.0.0:10909->10909/tcp, 0.0.0.0:10911->10911/tcp   rmqbroker
052284e86012        apache/rocketmq-namesrv:4.3.0   "/bin/sh -c 'cd ${RO鈥?"   6 minutes ago       Up 6 minutes        0.0.0.0:9876->9876/tcp                               rmqnamesrv

 生成了三个镜像,其中两个服务NameServer 和 Broker 都是必须运行的。在生成今镜像的同时已经自动启动了。是不是非常简单

1. NameServer服务 默认端口:9876

2. Broker服务 两个端口都要开放:10909(Vip通道) 10911(默认通道)

 两个服务都启动后,这里要确认Broker是否已经注册到了NameServer上。

 

 

 

开始测试

  生产者和消费者在使用时都只需要连接到NameServer上,NameServer会自动路由。

  pom.xml

<rocketmq.version>4.0.0-incubating</rocketmq.version>
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>${rocketmq.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-common</artifactId>
    <version>${rocketmq.version}</version>
</dependency>

 

  生产者 

package com.wangwenjian.cloud.rocketmq;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;

public class Producer {

    public static void main(String[] args) throws Exception{
        /**
         * 一个应用创建一个Producer,由应用来维护此对象,可以设置为全局对象或者单例<br>
         * 注意:ProducerGroupName需要由应用来保证唯一<br>
         * ProducerGroup这个概念发送普通的消息时,作用不大,但是发送分布式事务消息时,比较关键,
         * 因为服务器会回查这个Group下的任意一个Producer
         */
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        producer.setNamesrvAddr("127.0.0.1:9876");

        producer.setInstanceName("Producer");
        //不使用VIP通道(10909端口), 使用(10911端口)
        producer.setVipChannelEnabled(false);


        /**
         * Producer对象在使用之前必须要调用start初始化,初始化一次即可<br>
         * 一个Producer对象可以发送多个message
         * 注意:切记不可以在每次发送消息时,都调用start方法
         */
        producer.start();

        
         try {

             /**
              *  
              * 1。 Topic 是一种消息的逻辑分类,比如说你有订单类的消息,也有库存类的消息,就需要进行分类,
              *  一个是订单 Topic 存放订单相关的消息,一个是库存 Topic 存储库存相关的消息
              *
              * 2。 Tag 标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。
              *
              * 3。 剩余就是键值对,一个 key 对应一个消息 body。
              */
             Message msg = new Message("dingdantopic",// topic
                         "TagA",// tag
                         "OrderID001",// key
                         ("Hello MetaQ").getBytes());// body
             
             /**
              * 注意:send方法是同步调用,只要不抛异常就标识成功。但是发送成功也可会有多种状态,
              * 例如消息写入Master成功,但是Slave不成功,这种情况消息属于成功,但是对于个别应用如果对消息可靠性要求极高,
              * 需要对这种情况做处理。另外,消息可能会存在发送失败的情况,失败重试由应用来处理。
              */
             SendResult sendResult = producer.send(msg);
             
             System.out.println(sendResult);
             
                 
         } catch (Exception e) {
             e.printStackTrace();
         }

        /**
         * 应用退出时,要调用shutdown来清理资源,关闭网络连接,从MetaQ服务器上注销自己
         * 注意:我们建议应用在JBOSS、Tomcat等容器的退出钩子里调用shutdown方法
         */
        producer.shutdown();
    }
}

  消费者 

package com.wangwenjian.cloud.rocketmq;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;

public class Consumer {

    /**
     * 当前例子是PushConsumer用法,使用方式给用户感觉是消息从RocketMQ服务器推到了应用客户端。<br>
     * 但是实际PushConsumer内部是使用长轮询Pull方式从MetaQ服务器拉消息,然后再回调用户Listener方法<br>
     */
    public static void main(String[] args) throws Exception {
        /**
         * 一个应用创建一个Consumer,由应用来维护此对象,可以设置为全局对象或者单例<br>
         * 注意:ConsumerGroupName需要由应用来保证唯一
         */
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
        /**
         * 设置NameServer地址,此处应改为实际NameServer地址,多个地址之间用;分隔
         * NameServer的地址必须有,但是也可以通过环境变量的方式设置,不一定非得写死在代码里
         */
        consumer.setNamesrvAddr("192.168.56.101:9876");

        // instancename 设置与否不影响
        // producer.setInstanceName("Consumber");
        //不使用VIP通道(10909端口), 使用(10911端口
         producer.setVipChannelEnabled(false);

        /**
         * 订阅指定 userTopic 下tags 分别等于 userTagA 或 userTagB 或 userTagC
         */
        consumer.subscribe("userTopic", "userTagA || userTagB || userTagC");
        /**
         * 订阅指定orderTopic下所有消息<br>
         * 注意:一个consumer对象可以订阅多个topic
         */
        consumer.subscribe("orderTopic", "*");

        // 设置消费者消费方式
        consumer.registerMessageListener(new MessageListenerConcurrently() {

            /**
             * 默认msgs里只有一条消息,可以通过设置consumeMessageBatchMaxSize参数来批量接收消息
             */
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(
                    List<MessageExt> msgs, ConsumeConcurrentlyContext context) {

                
                System.out.println(msgs + "" + context);

                MessageExt msg = msgs.get(0);
                if (msg.getTopic().equals("userTopic")) {

                    if (msg.getTags() != null && msg.getTags().equals("userTagA")) {

                        System.out.println(new String(msg.getBody()));

                    } else if (msg.getTags() != null && msg.getTags().equals("userTagB")) {

                        System.out.println(new String(msg.getBody()));

                    } else if (msg.getTags() != null && msg.getTags().equals("userTagC")) {

                        System.out.println(new String(msg.getBody()));
                    }
                } else if (msg.getTopic().equals("orderTopic")) {

                    System.out.println(new String(msg.getBody()));
                }

                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });

        
        /**
         * Consumer对象在使用之前必须要调用start初始化,初始化一次即可<br>
         */
        consumer.start();

        System.out.println("Consumer Started.");
    }
}

3. console(这个是一个web展示界面非必须,不在源码里,有需要可单独下载安装)

$ docker run --name rmqconsole -p 8180:8080 --link rmqnamesrv -e "JAVA_OPTS=-Drocketmq.namesrv.addr=rmqnamesrv:9876" -t styletang/rocketmq-console-ng

  打开页面没有报connection exception 并且看到集群下有broker 说明该界面工具已经完美连接到了nameserver。

常见异常

# 我们生产者要使用 usertopic, broker上没有该 topic。
error: No route info of this topic, usertopic

解决方法:
# 自己手动在broker上创建
# 启动broker时加上autoCreateTopicEnable=true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值