Rocketmq 入门--1_部属

本文档详细介绍了RocketMQ 4.3.0的集群部署步骤,包括NameServer、Broker(Master和Slave)、Producer及Consumer的角色和配置。文中提供了物理部署图,并指导如何修改配置、启动服务以及进行系统优化。测试表明,消息被正确地发送到不同MessageQueue并按预期消费。

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

入门文章:

看了那么多,讲的比较好的: https://m.aliyun.com/yunqi/articles/66110

比较全面的:https://blog.youkuaiyun.com/column/details/learningrocketmq.html

 

rocketmq中的节点

NameServer
NameServer是一个几乎无状态节点,所有人信息都一致,可集群部署,节点之间无任何信息同步。
记录消息元数据在那一个Broker上,通过NameServer将消息发送和消费指定到某一个具体的Broker上。

Broker
Broker 部署相对复杂,Broker分为Master和Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册 Topic 信息到所有 NameServer。


Producer
Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Broker Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。

Consumer
Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。

 

物理部署图:

 

集群部属 rocketmq 4.3.0

前提:安装 64bit jdk1.8+  

机器分配:
192.168.121.129 A    nameserver
192.168.121.130 B    broker1_master
192.168.121.131 C    broker1_slave
192.168.121.132 D    broker2_master
192.168.121.143 E    broker2_slave

修改hosts
192.168.121.129 A
192.168.121.130 B
192.168.121.131 C
192.168.121.132 D
192.168.121.143 E

下载已经编译的bin-release 版本:
https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.3.0/rocketmq-all-4.3.0-bin-release.zip

解压:
# unzip rocketmq-all-4.3.0-bin-release.zip 

目录说明
benchmark    用于基准测试
bin            可执行的命令
conf        配置文件(包含broker 配置,和日志文件的配置,里面包含了主从同步/异步的配置模板等)     
lib           依赖的包
LICENSE  
NOTICE  
README.md

修改配置  (参考官网配置: http://rocketmq.apache.org/docs/rmq-deployment/)

使用conf/2m-2s-async配置模板(2主2从,异步组刷盘方式):

broker-a.properties  
broker-a-s.properties  
broker-b.properties  
broker-b-s.properties

4个文件添加如下配置,指定nameserver,其它的参考官网解释不用修改。
namesrvAddr=a:9876

修改jvm 启动参数(http://rocketmq.apache.org/docs/system-config/)
修改内存大小

nameserver 启动文件 bin/runserver.sh
修改内存大小 -Xms512m -Xmx512m -Xmn256m

broker 启动文件  bin/runbroker.sh
修改内存大小 -Xms512m -Xmx512m -Xmn256m

注:runserver.sh   runbroker.sh  有 一个 -Xloggc 配置,指定gc日志的,可以指定自己的目录


修改rocketmq 日志目录,配置文件在 conf/ 文件夹下:

默认在目录:${user.home}/logs/rocketmqlogs 下,这里使用默认的
创建日志目录:
cd ~ && mkdir -p logs/rocketmqlogs

把配置好的分发到各机器上:
scp -r rocketmq root@B:/root
scp -r rocketmq root@C:/root
scp -r rocketmq root@D:/root
scp -r rocketmq root@E:/root


启动:
启动nameserver,在机器A 上执行
# nohup sh bin/mqnamesrv &
查看启动的进程:
# jps
2592 Jps
2548 NamesrvStartup

分别启动broker
b服务器上启动:
# nohup sh bin/mqbroker -c conf/2m-2s-async/broker-a.properties &
c服务器上启动:
# nohup sh bin/mqbroker -c conf/2m-2s-async/broker-a-s.properties &
d服务器上启动:
# nohup sh bin/mqbroker -c conf/2m-2s-async/broker-b.properties &
e服务器上启动:
# nohup sh bin/mqbroker -c conf/2m-2s-async/broker-b-s.properties &
 

都是java进程,可以使用jps查看是否启动
注:rocketmq 进行系统优化,使用NOOP调度算法
$ sudo ~/rocketmq/bin/os.sh

测试:

代码

/**
 * mq 配置信息
 */
public class RocketMQConfigure {

    /**
     * name server 地址,多个用分号隔开
     */
    public static String NAMESRV_ADDR = "192.168.121.129:9876";

    /**
     * broker 组名
     */
    public static String BROKER_GROUP = "test_broker_group";

    /**
     * consumer 组名
     */
    public static String CONSUMER_GROUP = "test_consumer_group";


    /**
     * 消息主题
     */
    public static String MESSAGE_TOPIC ="test_message_topic";

    /**
     * 消息key 前缀,消息key一般是业务的主键
     */
    public static String MESSAGE_KEY_PREFIX = "message_key_prefix:";

    /**
     * 消息tag
     */
    public static String MESSAGE_TAG_A ="test_tag_a";
    public static String MESSAGE_TAG_B ="test_tag_b";


    /**
     * 默认编码格式
     */
    public static String DEFAULT_CHARSET="UTF-8";

}

生产者:

/**
 * 简单生产者
 */
public class SimpleProducer {

    public static void main(String[] args)  throws Exception{

        //使用 默认生产者,参考官网
        DefaultMQProducer producer = new DefaultMQProducer(RocketMQConfigure.BROKER_GROUP);
        //设置name server 地址
        producer.setNamesrvAddr(RocketMQConfigure.NAMESRV_ADDR);

        try {
            //启动生产者
            producer.start();

            for (int i = 0; i < 10; i++) {
                //创建一个消息,指定 topic , tags, 消息体
                Message msg = new Message(RocketMQConfigure.MESSAGE_TOPIC ,RocketMQConfigure.MESSAGE_TAG_A,
                        ("Hello RocketMQ " +i).getBytes(RocketMQConfigure.DEFAULT_CHARSET));

                //发送消息到一个broker,默认是 SYNC即可靠消息传输。还有异步传输,单向传输 可以参考官网
                SendResult sendResult = producer.send(msg);
                System.out.printf("%s%n", sendResult);

            }
            
        } catch (MQClientException e) {
            e.printStackTrace();
        } finally {
            producer.shutdown();
        }
    }
    
}

消费者:

public class SimpleConsumer {

    public static void main(String[] args)  throws Exception{

        //使用 默认生产者,参考官网
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(RocketMQConfigure.CONSUMER_GROUP);
        consumer.setNamesrvAddr(RocketMQConfigure.NAMESRV_ADDR);

        //订阅消息,指定订阅消息的 topic 和 tag
        consumer.subscribe(RocketMQConfigure.MESSAGE_TOPIC, RocketMQConfigure.MESSAGE_TAG_A);

        //设置集群消息
        consumer.setMessageModel(MessageModel.CLUSTERING);

        //设置消息监听,这里使用并发的监听 MessageListenerConcurrently,还有一个顺序的监听 MessageListenerOrderly,可以查看源码
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                msgs.forEach(msg->{
                    System.out.println("消费消息:"+new String(msg.getBody()));
                });
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
    }
}

测试结果:
先启动消费者
再启动生产者,生产者打印输出如下:
SendResult [sendStatus=SEND_OK, msgId=C0A8E28000002A9F0000000000000714, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-a, queueId=0], queueOffset=4]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28000002A9F00000000000007AB, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-a, queueId=1], queueOffset=4]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28000002A9F0000000000000842, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-a, queueId=2], queueOffset=2]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28000002A9F00000000000008D9, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-a, queueId=3], queueOffset=2]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28F00002A9F00000000000004B8, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-b, queueId=0], queueOffset=2]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28F00002A9F000000000000054F, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-b, queueId=1], queueOffset=2]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28F00002A9F00000000000005E6, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-b, queueId=2], queueOffset=2]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28F00002A9F000000000000067D, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-b, queueId=3], queueOffset=2]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28000002A9F0000000000000970, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-a, queueId=0], queueOffset=5]
SendResult [sendStatus=SEND_OK, msgId=C0A8E28000002A9F0000000000000A07, messageQueue=MessageQueue [topic=test_message_topic, brokerName=broker-a, queueId=1], queueOffset=5]

 

测试总结:

1、从日志中可以看出,每个消息被分配到不同的 MessageQueue。
猜测:每个broker 管理多个 MessageQueue,这些MessageQueue 又通过 message topic 分组。所有就有了 MessageQueue [topic=test_message_topic, brokerName=broker-a, queueId=1] 类似这样的结构(知道的可以在留言)。

2、在consumer 中消费消息也可以通过定义 MessageQueue 拉取消息消费,代码如下:
DefaultMQPullConsumer pullConsumer  = new DefaultMQPullConsumer();
pullConsumer.fetchConsumeOffset(MessageQueue mq, boolean fromStore);

3、通过这两个方法获取 topic 的 MessageQueue,返回的都是Set<MessageQueue>
pullConsumer.fetchMessageQueuesInBalance(String topic)

pullConsumer.fetchSubscribeMessageQueues(String topic)

 

注:
1、如果提示没有topic 可以手动创建topic

创建一个topic (mqadmin 参考官网 http://rocketmq.apache.org/docs/cli-admin-tool/)
# ./mqadmin updateTopic -n 192.168.121.129:9876 -c DefaultCluster  -t test_message_topic

2、关闭防火墙

 

 

rocketmq 设计

数据存储结构:

 

consumerQueue 存储的信息是消息在commitlong中位置的信息如下:

8字节  commitlog offset

4字节  消息大小

8字节  MessageTag hashcode


发送消息: topic 被分配到多个broker(nameserver 会记录topic 分配到的brocker上),在broker当消息被写到commitlog 后,会在topic 分配的一个consumerQueue写上消息的信息(写的方式有几种),消费都通过写入consumerQueue的信息就可以打到commitlog中对应的真实消息。

消费消息:
consume消费消息时会先从nameserver 获取topic被分配的broker,然后遍历broker 的 consumer Queue,在Broker端进行Message Tag比对(这里比的是 tag 的hash code值),如果存储的Message Tag与订阅的Message Tag不符合,则跳过,继续比对下一个,符合则传输给Consumer。
Consumer收到过滤后的消息后,再比对MessageTag字符串。
注:这里只是通过 tag 获取消息,后面还有其它的获取消息的方式。

优点:
    1、consumer Queue 存储的是messageTag 的hashCode,这样存储定长,节约空间
    2、过滤不访问commitlog,过滤快,消息堆积过滤也很快
    3、consumer 会对消息的tag值的过虑,解决了hash冲突

可见rocketmq 的负载是通过consumer Queue来完成的。发送消息消息被分配到brocker 的consumer Queue上,消费消息时遍历consumer Queue。

其它的特点参考: https://m.aliyun.com/yunqi/articles/66110

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值