RocketMQ入门三:(整合springboot)单机部署&集群部署

949 阅读15分钟

springBoot+RocketMQ示例

大家可以按照上一节的操作:搭建本地环境,启动第一个demo,简单的把nameserver和 broker 启动一下,然后按照下面的代码整合springboot体验一下发送,消费消息。

结尾列出了所有的问题,遇到问题可以去看一下,没有提到的发出来我们一起学习,一起解决

创建springboot项目或者在自己的项目中的pom文件中引入依赖

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot</artifactId>
    <version>2.2.0</version>
</dependency>

引入依赖之后我们需要配置一下rocketmq的配置信息,打开我们的application.yml

rocketmq:
 name-server: 8.***.***.**:9876;  #nameserver IP地址
 producer:
   group: TEST_GROUP # 指定group
   send-message-timeout: 3000 # 消息发送超时时长,默认3s
   retry-times-when-send-failed: 3 # 同步发送消息失败重试次数,默认2
   retry-times-when-send-async-failed: 3 # 异步发送消息失败重试次数,默认2

定义生产者

首先自定义一个生产者。用来投递消息。

@Slf4j
@Component
public class MQProducerService {

   // 直接注入使用,用于发送消息到broker服务器
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

   /**
     * 普通发送
     */
    public void send(String body,String topic) {
        rocketMQTemplate.convertAndSend(topic , body);
//        rocketMQTemplate.send(topic + ":tag1", MessageBuilder.withPayload(user).build()); // 等价于上面一行
    }

    /**
     * 发送同步消息(阻塞当前线程,等待broker响应发送结果,这样不太容易丢失消息)
     * (msgBody也可以是对象,sendResult为返回的发送结果)
     */
    public SendResult sendMsg(String body,String topic) {
        SendResult sendResult = rocketMQTemplate.syncSend(topic, MessageBuilder.withPayload(body).build());
        log.info("【sendMsg】sendResult={}", JSON.toJSONString(sendResult));
        return sendResult;
    }

   /**
     * 发送异步消息(通过线程池执行发送到broker的消息任务,执行完后回调:在SendCallback中可处理相关成功失败时的逻辑)
     * (适合对响应时间敏感的业务场景)
     */
    public void sendAsyncMsg(String body,String topic) {

        rocketMQTemplate.asyncSend(topic, MessageBuilder.withPayload(body).build(), new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                //发送成功处理...
            }
            @Override
            public void onException(Throwable throwable) {
              //发送失败处理...
        
            }
        });
    }
    
   /**
     * 发送延时消息(上面的发送同步消息,delayLevel的值就为0,因为不延时)
     * 在start版本中 延时消息一共分为18个等级分别为:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
     */
    public void sendDelayMsg(String body,String topic, int delayLevel) {
        rocketMQTemplate.syncSend(topic, MessageBuilder.withPayload(body).build(), 3000, delayLevel);
    }

    /**
     * 发送单向消息(只负责发送消息,不等待应答,不关心发送结果,如日志)
     */
    public void sendOneWayMsg(String body,String topic) {
        rocketMQTemplate.sendOneWay(topic, MessageBuilder.withPayload(body).build());
    }
    
   /**
     * 发送带tag的消息,直接在topic后面加上":tag"
     */
    public SendResult sendTagMsg(String body,String topic) {
        return rocketMQTemplate.syncSend(topic, MessageBuilder.withPayload(body).build());
    }

}

代码中body就是消息体,如果你发消息之前是一个/一组对象,可以转换成json格式,当然也可以把上面的body修改成自己要传的对象比如Usertopic就是要发送到那个主题上。

 public void sendOneWayMsg(User user,String topic) {
        rocketMQTemplate.sendOneWay(topic, MessageBuilder.withPayload(user).build());
    }

上面就是一个Producer示例,里面包含了多种发送消息的模式,当然RocketMQ给我们提供的不止这些,大家可以去官方文档看一下。

定义消费者

@Slf4j
// MessageExt:是一个消息接收通配符,不管发送的是String还是对象,都可接收,当然也可以像上面明确指定类型(我建议还是指定类型较方便)
//,messageModel = MessageModel.BROADCASTING
@Service
@RocketMQMessageListener(topic = "TEST_TOPIC", consumerGroup = "TEST_Group")
public class TaxInfoConsumerService implements RocketMQListener<MessageExt> {
   
    @Override
    public void onMessage(MessageExt message) {
        System.out.println("线程" + Thread.currentThread() + "内容为:"
                + new String(message.getBody()) +
                "队列序号:" + message.getQueueId());
        //这里消费的消息可以写自己的业务逻辑代码,比如插入,删除,上传。。。

    }

这个就是简单的消费者示例。

@RocketMQMessageListener(topic = "TEST_TOPIC", consumerGroup = "Test_Group")

这行代码表示当前消费者监听了TEST_TOPIC上的信息,消费者组的名字就叫做Test_Group. 如果有生产者往TEST_TOPIC上投递消息,就会被当前消费者感知,并且去消费指定Topic上的信息。

发送消息

//使用之前先注入我们刚创建的MQProducerService
@Inject
private MQProducerService mqProducerService;



......
//我自己有一个类TaxInfoBean,里面封装了我要传的数据:
TaxInfoBean taxInfoBean = new TaxInfoBean();
taxInfoBean.setSn(taxInfoSn.nextNo());
taxInfoBean.setOutTxNo(outTxNo);
taxInfoBean.setNotifyTimes(0);
taxInfoBean.setAmount(amount);
taxInfoBean.setIdCardNo(idCardNo);
taxInfoBean.setTaxNo(taxNo);
taxInfoBean.setInvoiceCode(invoiceCode);
taxInfoBean.setBankReceiptFiles(bankReceiptFiles);
taxInfoBean.setProofFiles(proofFiles);
taxInfoBean.setTaxProjectName(taxProjectName);
String taxInfoData = JSON.toJSONString(taxInfoBean);

//重点:使用刚刚创建的mqProducerService的异步发送方式发送消息。
//taxinfoData是投递的消息,TEST_TOPIC代表我要投递到这个topic中
mqProducerService.sendAsyncMsg(taxInfoData, “TEST_TOPIC”);

大家可以自己创建一个测试类,测试一下,我这里只截取了部分代码。

启动测试方法,发送消息,我们打开控制台就会看到输出了:

线 程 Thread[ConsumeMessageThread_14,5,main]内 容 为 :{"amount":999.99,"idCardNo":"410425128710026151","invoiceCode":"304990000123400000001014","invoiceType":1,"notifyTimes":0,"outTxNo":"BZD44V5Dh_V_vQ7h9fEXa","settlementType":0,"sn":"1123041202077","taskStatus":"PROCESSING","taxNo":"91110105MA01R54K8A2","taxProjectName":"20230491110105MA01R54K8A2"}队 列 序 号 :8

没错,这就是消费者已经成功消费了,并把整个message打印了出来,我们的消息内容是 message.getBody()的所有内容。

我们可以在消费者里面加上自己业务代码。

springboot+rocketmq单机部署(centos7+docker)

想了一下还是跟大家一起用docker先单机部署一下 熟悉流程之后,后面的集群部署也就非常简单了。

首先打开我们的Linux环境,启动docker(没有安装docker的可以搜一下,直接安装就行。)

启动doker: systemctl start docker

搜索镜像

docker search rocketmq

图片.png

拉取镜像

docker pull rocketmqinc/rocketmq

一般情况下拉取的就是最新版本。

这个时候rocketmq已经准备好了,我们回忆下上一节的启动过程,这里有些类似的地方,我们创建一个存放nameserver的数据目录:

mkdir -p /docker/rocketmq/nameserver/logs /docker/rocketmq/nameserver/store

同理,我们也提前创建存放broker配置信息目录,我们需要单独创建一个存放broker配置信息的文件目录

mkdir -p /docker/rocketmq/data/broker-master/conf
mkdir -p /docker/rocketmq/data/broker-slave/conf

能看出来,这两个文件夹就是代表broker的一主一从。除了这个conf文件夹,我们还要另外创建两个文件夹

一主一从: 前面大家都知道了broker可以配置主从节点,一个master一个slavemaster节点会进行异步/同步刷盘到slave上,这也保证了如果某个broker挂了,slave能够迅速顶上。

RocketMQ支持两种刷盘方式:同步刷盘异步刷盘。同步刷盘是指在消息发送的同时将消息写入磁盘,确保消息不会丢失,但是会降低消息发送的速度。异步刷盘是指将消息先写入内存缓存,然后再定时或者触发条件下将缓存中的消息写入磁盘,这样可以提高消息发送的速度,但是可能会造成消息丢失的风险。

mkdir -p /docker/rocketmq/data/broker-master/store 
mkdir -p /docker/rocketmq/data/broker-master/logs  
mkdir -p /docker/rocketmq/data/broker-slave/store 
mkdir -p /docker/rocketmq/data/broker-slave/logs  

整体的架构就是:

➜  docker/rockermq
.
├── data
│   ├── broker-master
│       ├── conf
│       ├── store
│       └── logs
│   ├── beoker-slave
│       ├── conf
│       ├── store
│       └── logs
└── nameserver
    ├── store
    └── logs

初始的文件夹已经创建好了,我们要先启动nameserver.

docker run -d --restart=always --name rmqnamesrv --privileged=true -p 9876:9876  -v /docker/rocketmq/nameserver/logs:/root/logs -v /docker/rocketmq/nameserver/store:/root/store -e "MAX_POSSIBLE_HEAP=100000000" rocketmqinc/rocketmq sh mqnamesrv
  • 参数说明
参数说明
-d以守护进程的方式启动
- -restart=alwaysdocker重启时候容器自动重启
- -name rmqnamesrv把容器的名字设置为rmqnamesrv
-p 9876:9876把容器内的端口9876挂载到宿主机9876上面
-v /docker/rocketmq/nameserver/logs:/root/logs目录挂载
-v /docker/rocketmq/nameserver/store目录挂载
rmqnamesrv容器的名字
-e “MAX_POSSIBLE_HEAP=100000000”设置容器的最大堆内存为100000000
rocketmqinc/rocketmq使用的镜像名称
sh mqnamesrv启动namesrv服务

配置broker

我们有关broker的文件夹之前已经创建好了,但是缺少配置文件broker.conf

创建masterbroker.conf

brokerName = brokera-master
brokerId = 0   #id为0代表当前是主节点(master)
deleteWhen = 04
fileReservedTime = 48
namesrvAddr = 8.*.*.*:9876;  #你的nameserver地址
brokerRole = ASYNC_MASTER  
flushDiskType = ASYNC_FLUSH
autoCreateTopicEnable = false  #这个强烈建议设置成false
brokerIP1 = 当前服务器ip地址

然后把这个文件放到data/broker-master/conf里面

创建slavebroker.conf

brokerName = brokera-slave
brokerId = 1
deleteWhen = 04
fileReservedTime = 48
namesrvAddr = 8.*.*.*:9876;
brokerRole = SLAVE
flushDiskType = ASYNC_FLUSH
autoCreateTopicEnable = false
brokerIP1 = 当前服务器ip地址

然后把这个文件放到data/broker-slave/conf里面

autoCreateTopicEnable代表是否开启自动创建topic,这里大家设置成false就行,如果只是自己测试一下那就无所谓,发不到线上的话为了避免创建无用的topic,浪费资源,建议还是关掉。

整体目录架构

➜  docker/rockermq
.
├── data
│   ├── broker-master
│       ├── conf
│           ├──broker.conf
│       ├── store
│       └── logs
│   ├── beoker-slave
│       ├── conf
│           ├──broker.conf
│       ├── store
│       └── logs
└── nameserver
    ├── store
    └── logs
   

启动broker

这里大家一定要谨慎,这里最容易出错,大家启动的时候一定提前看看自己的命令是否正确 我们要启动两次broker(一主一从) 启动brokera-master

docker run -d --restart=always --name brokera-master --link rmqnamesrv:namesrv -p 10911:10911 -p 10909:10909 --privileged=true -v /docker/rocketmq/data/broker-master/logs:/root/logs -v /docker/rocketmq/data/broker-master/store:/root/store -v /docker/rocketmq/data/broker-master/conf/broker.conf:/opt/docker/rocketmq/data/broker-master/conf/broker.conf -e "NAMESRV_ADDR=8.***:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/docker/rocketmq/data/broker-master/conf/broker.conf

大家把 NAMESRV_ADDR=8.*:9876",改成自己的nameserver地址。

如果前面的所有步骤都跟我的一样,正常情况下是没问题的,如果前面创建文件夹那里并没有跟我的一模一样,那你就要把命令改一下,文件目录做一下修改才行。

完事之后输入

docker ps -a 查看所有的已经启动的容器。

图片.png 可以看到STATUS那里是启动成功的 那么master节点的broker就已经启动成功了,接下来启动slave

docker run -d --restart=always --name brokera-slave --link rmqnamesrv:namesrv -p 10124:10911 -p 10623:10909 --privileged=true -v /docker/rocketmq/data/broker-slave/logs:/root/logs -v /docker/rocketmq/data/broker-slave/store:/root/store -v /docker/rocketmq/data/broker-slave/conf/broker.conf:/opt/docker/rocketmq/data/broker-slave/conf/broker.conf -e "NAMESRV_ADDR=8.*:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/docker/rocketmq/data/broker-slave/conf/broker.conf

NAMESRV_ADDR的地址换成自己的nameserver 地址。

图片.png 可以看到一主一从都已经启动成功了

安装可视化控制台

启动完brokernameserver之后,我们启动可视化控制台。

拉取镜像

docker pull pangliang/rocketmq-console-ng

直接启动控制台 启动之前大家把nameserv.addr 的地址改成自己的nameserver的IP地址

docker run -d --restart=always --name rmqadmin -e "JAVA_OPTS=-Drocketmq.namesrv.addr=122.*.*.*:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 pangliang/rocketmq-console-ng

图片.png

完事之后直接打开服务器ip:8080 直接启动就能看到熟悉的页面

图片.png 可以看到一主一从,我的名字是brokera-master-a,brokera-slave-a

如果没有这两条broker记录大家一定要检查自己的nameserver地址有没有配置,着重检查broker.conf,和启动容器命令,容器命令有多个文件目录,一定要跟自己创建的对的上。

springboot使用

整合Springboot和上面的过程一模一样 ,application.ymlnamesrv-addr可能要改成自己Linux配置之后的地址, 其他代码不用改。

springboot+rocketmq集群部署(centos7+docker)

趁热打铁,上面完成了单机部署,我这里有两台服务器,我采用的是2个nameserver,broker是2主2从。 每台服务器上是1个nameserver和broker(一主一从)

我们按照上面的步骤每台服务器上都重复创建好存放nameserver和broker的文件目录,两台服务器都是如此

➜  docker/rockermq
.
├── data
│   ├── broker-master
│       ├── conf
│           ├──broker.conf
│       ├── store
│       └── logs
│   ├── beoker-slave
│       ├── conf
│           ├──broker.conf
│       ├── store
│       └── logs
└── nameserver
    ├── store
    └── logs
   

两台服务器都拉取rocketmq镜像,然后依次启动容器(两台服务器命令一样

docker run -d --restart=always --name rmqnamesrv --privileged=true -p 9876:9876 -v /docker/rocketmq/nameserver/logs:/root/logs -v /docker/rocketmq/nameserver/store:/root/store -e "MAX_POSSIBLE_HEAP=100000000" rocketmqinc/rocketmq sh mqnamesrv

这样就启动了2台nameserver

接下来配置broker.conf

第一台服务器上的一主一从broker.conf

brokerClusterName = XgshDefaultCluster
brokerName = broker-master-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
namesrvAddr = 8.*.*.*:9876;8.*.*.*:9876 #大家可以看到这里有两个nameserver IP地址,用分号隔开。
brokerRole = ASYNC_MASTER  
flushDiskType = ASYNC_FLUSH
autoCreateTopicEnable = false  #关闭自动创建Topic
brokerIP1 = 本机服务器IP
brokerClusterName = XgshDefaultCluster
brokerName = broker-slave-a
brokerId = 1 
deleteWhen = 04
fileReservedTime = 48
namesrvAddr = 8.*.*.*:9876;8.*.*.*:9876
brokerRole = SLAVE
flushDiskType = ASYNC_FLUSH
autoCreateTopicEnable = false
brokerIP1 = 本机服务器IP

第二台服务器上的一主一从broker.conf

brokerClusterName = XgshDefaultCluster
brokerName = broker-master-b
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
namesrvAddr = 8.*.*.*:9876;8.*.*.*:9876 #大家可以看到这里有两个nameserver IP地址,用分号隔开。
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
autoCreateTopicEnable = false
brokerIP1 = 本机服务器IP


brokerClusterName = XgshDefaultCluster
brokerName = broker-slave-b
brokerId = 1
deleteWhen = 04
fileReservedTime = 48
namesrvAddr = 8.*.*.*:9876;8.*.*.*:9876 #大家可以看到这里有两个nameserver IP地址,用分号隔开。
brokerRole = SLAVE
flushDiskType = ASYNC_FLUSH
autoCreateTopicEnable = false
brokerIP1 = 本机服务器IP

  • 参数说明
参数说明
brokerClusterNamebroker名字(都用一样的就可以)
brokerName当前broker的名字
brokerId当前broker的ID(0可以理解为master,1可以理解为slave)
namesrvAddr当前broker要注册到哪些nameserver上
brokerRole当前broker角色(SLAVE就是代表是从节点)
flushDiskType刷盘方式
autoCreateTopicEnable是否启动自动创建topic默认为true
brokerIP1本机服务器IP

启动broker

第一台服务器(命令中的NAMESRV_ADDR改成自己服务器启动的2个,中间用分号隔开)

docker run -d --restart=always --name broker-master01 --link rmqnamesrv:namesrv -p 10911:10911 -p 10909:10909 --privileged=true -v /docker/rocketmq/data/broker-master/logs:/root/logs -v /docker/rocketmq/data/broker-master/store:/root/store -v /docker/rocketmq/data/broker-master/conf/broker.conf:/opt/docker/rocketmq/data/broker-master/conf/broker.conf -e "NAMESRV_ADDR=8.*.*.*:9876;8.*.*.*:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/docker/rocketmq/data/broker-master/conf/broker.conf

docker run -d --restart=always --name broker-slave01 --link rmqnamesrv:namesrv -p 10124:10911 -p 10623:10909 --privileged=true -v /docker/rocketmq/data/broker-slave/logs:/root/logs -v /docker/rocketmq/data/broker-slave/store:/root/store -v /docker/rocketmq/data/broker-slave/conf/broker.conf:/opt/docker/rocketmq/data/broker-slave/conf/broker.conf -e "NAMESRV_ADDR=8.*.*.*:9876;8.*.*.*:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/docker/rocketmq/data/broker-slave/conf/broker.conf

第二台服务器(命令中的NAMESRV_ADDR改成自己服务器启动的2个,中间用分号隔开)

docker run -d --restart=always --name broker-master02 --link rmqnamesrv:namesrv -p 10911:10911 -p 10909:10909 --privileged=true -v /docker/rocketmq/data/broker-master/logs:/root/logs -v /docker/rocketmq/data/broker-master/store:/root/store -v /docker/rocketmq/data/broker-master/conf/broker.conf:/opt/docker/rocketmq/data/broker-master/conf/broker.conf -e "NAMESRV_ADDR=8.*.*.*:9876;8.*.*.*:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/docker/rocketmq/data/broker-master/conf/broker.conf 

docker run -d --restart=always --name broker-slave02 --link rmqnamesrv:namesrv -p 10124:10911 -p 10623:10909 --privileged=true -v /docker/rocketmq/data/broker-slave/logs:/root/logs -v /docker/rocketmq/data/broker-slave/store:/root/store -v /docker/rocketmq/data/broker-slave/conf/broker.conf:/opt/docker/rocketmq/data/broker-slave/conf/broker.conf -e "NAMESRV_ADDR=8.*.*.*:9876;8.*.*.*:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/docker/rocketmq/data/broker-slave/conf/broker.conf

broker启动成功,我们通过命令看一下(2台服务器都看一下)

docker ps -a

第一台服务器:

图片.png 第二台服务器: 图片.png 大家看一下STATUS是都启动成功。

启动控制台

在其中一台服务器拉取可视化界面镜像(只用在其中一台启动就可以了)

docker pull pangliang/rocketmq-console-ng

启动可视化控制台的命令。(namesrv.addr 后面的ip地址设置为两个。)

docker run -d --restart=always --name rmqadmin -e "JAVA_OPTS=-Drocketmq.namesrv.addr=122.*.*.*:9876;122.*.*.*:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 pangliang/rocketmq-console-ng

访问服务器IP:8080能看到下面的就代表成功了!!

图片.png

整合springboot

我们只需要在applicatiom.yml文件中修改为2个nameserver地址用分号隔开

rocketmq:
  name-server: 8.*.*.*:9876;8.*.*.*:9876 # 两个nameserver访问地址
  producer:
    group: Pro_Group # 必须指定group
    send-message-timeout: 3000 # 消息发送超时时长,默认3s
    retry-times-when-send-failed: 3 # 同步发送消息失败重试次数,默认2
    retry-times-when-send-async-failed: 3 # 异步发送消息失败重试次数,默认2

启动项目,我们根据自己的需要去创建Topic,然后通过开篇的示例去发送消息到消费者消费消息过程是不变的

问题:

  1. 可视化界面一直加载不出来

    因为我用的是阿里云服务器,所以访问不了大概率是8080这个端口号没有加安全组,大家进入到阿里云找到服务器,添加安全组,端口号8080,建议大家设置一下授权对象,然后把自己本机ip输入进去,返回浏览器刷新。就能访问了。

  2. 老问题,(2主2从)broker不显示

    首先我们使用docker ps -a 查看所有启动的容器,大家可以看下自己启动的broker,看一下STATUS如果不是Up 6 hours这种的,而是很长一串,那就是broker启动失败了,还是一定要看启动命令和broker.conf每一行配置是否正确,nameserver地址是否正确!

结语

后面我会专门把所有的问题总结一下,发一篇文章。本节篇幅很长,感谢您的耐心阅读,入门的课程到这里就结束了,后续我会跟大家一起针对优化进行更多的配置写成文章一起学习