RocketMQ学习(一)——基本概念和集群搭建

RocketMQ概念模型

Producer: 消息生产者,负责生产消息。
Consumer: 消息消费者,负责异步消费消息。
Push Consumer: Consumer的一种,需要向Consumer对象注册监听。
Pull Consumer: Consumer的一种,需要主动请求Broker拉取消息。
Producer Group: 生产者集合,一般用于发送一类消息。创建生产者时必须指定,用于事务消费的消息回查。
Consumer Group: 消费者集合,一般用于接受一类消息进行消费。创建消费者时必须指定。
Broker: MQ消息服务(中转角色,用于消息存储与生产消费转发)。

RocketMQ源码下载

git地址:https://github.com/apache/rocketmq/

源码结构:
在这里插入图片描述
rocketmq-broker:主要的业务逻辑,消息收发,主从同步,pagecache。
rocketmq-client:客户端接口,比如生产者和消费者。
rocketmq-example:示例,比如生产者和消费者。
rocketmq-common:公共数据结构等等。
rocketmq-distribution:编译模块,编译输出等。
rocketmq-filter:进行Broker过滤不感兴趣的消息传输,减小带宽压力。
rocketmq-logappender、rocketmq-logging:日志相关。
rocketmq-namesrv:Namesrv服务,用于服务协调。
rocketmq-openmessaging:对外提供服务。
rocketmq-remoting:远程调用接口,封装Netty底层通信。
rocketmq-srvutil:提供一些公用的工具方法,比如解析命令行参数。
rocketmq-store:消息存储。
rocketmq-tools:管理工具,比如有名的mqadmin工具。

RocketMQ集群方式

在Metaq1.x/2.x的版本中,分布式协调采用的是Zookeeper,而RocketMQ自己实现了一个NameServer,所以RocketMQ启动时,先启动对应的NameServer。
推荐的几种 Broker 集群部署方式,这里的Slave 不可写,但可读,类似于 Mysql主备方式。

单个Master

很显然,这种方式风险较大,一旦Broker重启或者宕机时,会导致整个服务不可用,不建议线上环境使用。

多Master模式

一个集群无Slave,全是Master,例如2个Master或者3个Master。
优点: 配置简单,单个Master宕机或重启维护对应用无影响,在磁盘配置为RAID10时,即使机器宕机不可恢复情况下,由于RAID10磁盘非常可靠,消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢)。性能最高。
缺点: 单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性会受到影响。

多Master多Slave模式,异步复制

每个Master配置一个Slave,有多对Master-Slave,HA采用异步复制方式,主备有短暂消息延迟,毫秒级。
优点: 即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,因为Master宕机后,消费者仍然可以从Slave消费,此过程对应用透明,不需要人工干预。性能同多Master模式几乎一样。主节点上线后会自动拉去从节点的offest信息,不会重复消费到slave中已经消费的消息。
缺点: Master宕机,磁盘损坏情况,会丢失少量消息。

多Master多Slave模式,同步双写

每个Master配置一个Slave,有多对Master-Slave,HA采用同步双写方式,主备都写成功,向应用返回成功。
优点: 数据与服务都无单点,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高。
缺点: 性能比异步复制模式略低,大约低10%左右,发送单个消息的RT会略高。

物理部署结构

以多Master多Slave模式为例,看一下RocketMQ物理部署结构:在这里插入图片描述

  1. Name Server
    NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。3.X之前的版本都是使用zk。
  2. Broker
    Broker 部署相对复杂,Broker分为Master和Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册 Topic 信息到所有 NameServer。
  3. Producer
    Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。
  4. Consumer
    Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。
双主集群部署

这里就以双主集群为例,进行搭建。(双主集群会部署搭建,多主或多主多从也自然没啥问题了)。

服务器环境

如果条件允许,NameServer和broker分别在单独的机器上部署,我是用自己的电脑建的虚拟机,在虚拟机中部署的,限于自己电脑的配置,NameServer和broker就放在一台机器上了。

序号IP角色模式
1192.168.152.129nameServer1,brokerServer1Master1
2192.168.152.130nameServer2,brokerServer2Master2
配置IP映射

在下面的文件中配置:

vi /etc/hosts 
192.168.152.129 rocketmq-nameserver1
192.168.152.129 rocketmq-master1
192.168.152.130 rocketmq-nameserver2
192.168.152.130 rocketmq-master2
生成tar包并安装

参考官网文档http://rocketmq.apache.org/docs/quick-start/

git clone https://github.com/apache/incubator-rocketmq.git
cd incubator-rocketmq
mvn -Prelease-all -DskipTests clean install -U
cd distribution/target/apache-rocketmq

在distribution/target/apache-rocketmq下有编译好的tar包 和zip包等。

创建数据文件夹
mkdir /usr/local/rocketmq/store
mkdir /usr/local/rocketmq/store/commitlog
mkdir /usr/local/rocketmq/store/consumequeue
mkdir /usr/local/rocketmq/store/index
修改配置文件

两台机器执行相同的操作,并注意,配置文件中的brokerName的值跟配置文件一致:

vim /home/rocketmq/conf/2m-noslave/broker-a.properties
vim /home/rocketmq/conf/2m-noslave/broker-b.properties

这里把常用的参数配置基本都列了出来,具体意思在注释里:

#所属集群名字
brokerClusterName=rocketmq-cluster
#broker名字
brokerName=broker-a
#0 表示Master, > 0 表示slave
brokerId=0
#nameServer 地址,分号分割
namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
#brokerIP1=192.168.152.129
#在发送消息时,自动创建服务器不存在的Topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许Broker自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=10911
#删除文件时间点,默认是凌晨4点
deleteWhen=04
#文件保留时间,默认48小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
destroyMapedFileIntervalForcibly=120000
redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/home/rocketmq/store
#commitLog存储路径
storePathCommitLog=/home/rocketmq/store/commitlog
#消费队列存储路径
storePathConsumeQueue=/home/rocketmq/store/consumequeue
#消息索引存储路径
storePathIndex=/home/rocketmq/store/index
#checkpoint 文件存储路径
storeCheckpoint=/home/rocketmq/store/checkpoint
#abort 文件存储路径
abortFile=/home/rocketmq/store/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
brokerRote=ASYNC_MASTER
#刷盘方式
flushDiskType=ASYNC_FLUSH
#发消息线程池数量
#sendMessageTreadPoolNums=128
#拉消息线程池数量
#pullMessageTreadPoolNums=128
修改启动脚本参数

调一下JVM,包括nameserver 和 broker。限于自己机器的配置,参数调小一下。但Rocketmq最少的堆是1g,否则无法启动。两台机器执行相同的操作。
打开一下runbroker配置文件:

vim /usr/local/rocketmq/bin/runbroker.sh

修改内容如下:

JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn512m -XX:PermSize=128m -XX:MaxPermSize=320m"

打开一下runserver配置文件:

vim /usr/local/rocketmq/bin/runserver.sh

修改内容如下:

JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn512m -XX:PermSize=128m -XX:MaxPermSize=320m"
启动

要先启动namerserver,再启broker,两台机器执行相同的操作。
启动nameserver

nohup sh /home/rocketmq/bin/mqnamesrv >/home/rocketmq/logs/mqnamesrv.log 2>&1 &

启动brokerserver(节点1用的是a.properties,节点2用的是b.properties)

nohup sh /home/rocketmq/bin/mqbroker -c /home/rocketmq/conf/2m-noslave/broker-a.properties >/home/rocketmq/logs/broker-a.log 2>&1 &
nohup sh /home/rocketmq/bin/mqbroker -c /home/rocketmq/conf/2m-noslave/broker-b.properties >/home/rocketmq/logs/broker-b.log 2>&1 &

用下面命令查看一下

jps

在这里插入图片描述

创建控制台

先将incubator-rocketmq-externals拉到本地,因为我们需要自己对rocketmq-console进行编译打包运行。

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

修改application.properties文件

rocketmq.config.isVIPChannel=false

通过命令行进入到rocketmq-console子目录,通过maven对其进行编译打包

mvn clean -DskipTests package

此时在rocketmq-console/target目录下生成了一个叫rocketmq-console-ng-1.0.0.jar的jar包。

cd /usr/local/software/rocketmq-externals/rocketmq-console/target/

接下来运行这个jar包,我们可以直接通过java -jar的方式运行

java -jar rocketmq-console-ng-1.0.0.jar --server.port=8889 --rocketmq.config.namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876

这里注意需要设置两个参数:–server.port为运行的这个web应用的端口,如果不设置的话默认为8080;–rocketmq.config.namesrvAddr为RocketMQ命名服务地址,如果不设置的话默认为“”。

PS
如果要搭主从,再次重申一遍Master与Slave在配置中的区别

Broker与Slave配对是通过指定相同的brokerName参数来配对,Master的BrokerId 必须是0,Slave的BrokerId必须是大于0的数。另外一个Master下面可以挂载多个Slave,同一Master下的多个Slave 通过指定不同的BrokerId来区分。

Broker 重启对客户端的影响

Broker 重启可能会导致正在发往这台机器的的消息发送失败,RocketMQ提供了一种优雅关闭Broker的方法,通过执行以下命令会清除Broker的写权限,过40s后,所有客户端都会更新Broker路由信息,此时再关闭Broker就不会发生发送消息失败的情况,因为所有消息都发往了其他 Broker。

sh mqadmin wipeWritePerm -b brokerName -n namesrvAddr
Master 与Slave的关系

RocketMQ的开源版本,Master宕机,Slave不能切换为Master,这里的Slave不可写,但可读,类似于 Mysql 主备方式。

Docker部署

1. 安装 Namesrv

拉取镜像

docker pull rocketmqinc/rocketmq:4.4.0

启动容器

docker run -d -p 9876:9876 -v {RmHome}/data/namesrv/logs:/root/logs -v {RmHome}/data/namesrv/store:/root/store --name rmqnamesrv -e "MAX_POSSIBLE_HEAP=100000000" rocketmqinc/rocketmq:4.4.0 sh mqnamesrv

注意事项

{RmHome} 要替换成你的宿主机想保存 MQ 的日志与数据的地方,通过 docker 的 -v 参数使用 volume 功能,把你本地的目录映射到容器内的目录上。否则所有数据都默认保存在容器运行时的内存中,重启之后就又回到最初的起点。

2. 安装 broker 服务器

拉取镜像

与上步是同一个镜像,如果上步完成,此步无需拉取

创建 broker.conf 文件

  1. 在 {RmHome}/conf 目录下创建 broker.conf 文件
  2. 在 broker.conf 中写入如下内
brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
brokerIP1 = {本地外网 IP}

brokerIP1 要修改成你自己宿主机的 IP

启动容器

docker run -d -p 10911:10911 -p 10909:10909 -v  {RmHome}/data/broker/logs:/root/logs -v  {RmHome}/rocketmq/data/broker/store:/root/store -v  {RmHome}/conf/broker.conf:/opt/rocketmq-4.4.0/conf/broker.conf --name rmqbroker --link rmqnamesrv:namesrv -e "NAMESRV_ADDR=namesrv:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq:4.4.0 sh mqbroker -c /opt/rocketmq-4.4.0/conf/broker.conf

注意事项

注意: {RmHome} 同上步一样,不再缀述。broker.conf 的文件中的 brokerIP1 是你的 broker 注册到 Namesrv 中的 ip。如果不指定他会默认取容器中的内网 IP。除非你的应用也同时部署在网络相通的容器中,本地或容器外就无法连接 broker 服务了,进而导致类似 RemotingTooMuchRequestException 等各种异常。

3. 安装 rocketmq 控制台

拉取镜像

docker pull pangliang/rocketmq-console-ng

启动容器

docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr={本地外网 IP}:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 -t pangliang/rocketmq-console-ng
生产者快速入门

RocketMQ默认会在topic不存在时自动创建,并且为topic生成4个MessageQueue。

public class Producer {
    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
        DefaultMQProducer producer = new DefaultMQProducer("test_quick_producer_name");

        producer.setNamesrvAddr(Const.NAMESRV_ADDR);

        producer.start();

        for(int i=0;i<5;i++) {
            //创建消息
            Message message = new Message("test_quick_topic", // 主题
                    "TagA", // 标签
                    "keyA", // 用户自定义的key,用户定义的唯一表示
                    ("Hello Rocket" + i).getBytes() // 消息内容实体(byte[])
            );
            //发送消息
            SendResult send = producer.send(message);
            System.out.println(send);
        }

        producer.shutdown();
    }
}
消费者快速入门

消费者需要订阅topic,设置从头消费还是从尾消费,这个只是在消费者首次启动遵循,在消费消息过后会将消费进度记录到broker和nameserver中,下次会遵循记录到位置开始消费。消费者可以返回状态,处理失败返回RECONSUME_LATER后,会自动进行重试,从5s到2h逐级递增重试15次,我们可以在消息中找到重试次数做后续到处理。

public class Consumer {
    public static void main(String[] args) throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test_quick_consumer_name");

        consumer.setNamesrvAddr(Const.NAMESRV_ADDR);

        //从尾部消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);

        consumer.subscribe("test_quick_topic", "*");


        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                MessageExt msg = list.get(0);
                try {
                    String topic = msg.getTopic();
                    String tags = msg.getTags();
                    String keys = msg.getKeys();
                    //模拟失败
                    if(keys.equals("key1")) {
                        System.out.println("消息消费失败。。。");
                        int a = 1/0;
                    }
                    String msgBody = new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET);
                    System.out.println("topic:" + topic + "tags:" + tags + "keys:" + keys + "msgBody:" + msgBody);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("重试次数:" + msg.getReconsumeTimes());
                    //设置超时次数
                    if(msg.getReconsumeTimes() == 3) {
                        //记录日志并且做补偿处理
                    }
                    return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
        System.out.println("consumer start...");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值