Spring Boot 中使用 RocketMQ

个人学习SpringBoot系列 RocketMQ篇

Github Link: https://github.com/panjianlong13/SpringBoot-SpringCloud/tree/master/spring-boot-rocketmq

RocketMQ介绍

RocketMQ 是什么

RcoketMQ 是一款低延迟、高可靠、可伸缩、易于使用的消息中间件。具有以下特性:

1.支持发布/订阅(Pub/Sub)和点对点(P2P)消息模型

2.在一个队列中可靠的先进先出(FIFO)和严格的顺序传递

3.支持拉(pull)和推(push)两种消息模式

4.单一队列百万消息的堆积能力

5.支持多种消息协议,如 JMS、MQTT 等

6.分布式高可用的部署架构,满足至少一次消息传递语义

7.提供 docker 镜像用于隔离测试和云集群部署

8.提供配置、指标和监控等功能丰富的 Dashboard


专业术语

Producer

消息生产者,生产者的作用就是将消息发送到 MQ,生产者本身既可以产生消息,如读取文本信息等。也可以对外提供接口,由外部应用来调用接口,再由生产者将收到的消息发送到 MQ。

Producer Group

生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。在这里可以不用关心,只要知道有这么一个概念即可。

Consumer

消息消费者,简单来说,消费 MQ 上的消息的应用程序就是消费者,至于消息是否进行逻辑处理,还是直接存储到数据库等取决于业务需要。

Consumer Group

消费者组,和生产者类似,消费同一类消息的多个 consumer 实例组成一个消费者组。

Topic

Topic 是一种消息的逻辑分类,比如说你有订单类的消息,也有库存类的消息,那么就需要进行分类,一个是订单 Topic 存放订单相关的消息,一个是库存 Topic 存储库存相关的消息。

Message

Message 是消息的载体。一个 Message 必须指定 topic,相当于寄信的地址。Message 还有一个可选的 tag 设置,以便消费端可以基于 tag 进行过滤消息。也可以添加额外的键值对,例如你需要一个业务 key 来查找 broker 上的消息,方便在开发过程中诊断问题。

Tag

标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。

Broker

Broker 是 RocketMQ 系统的主要角色,其实就是前面一直说的 MQ。Broker 接收来自生产者的消息,储存以及为消费者拉取消息的请求做好准备。

Name Server

Name Server 为 producer 和 consumer 提供路由信息。


RocketMQ 架构

Spring Boot 中使用 RocketMQ_第1张图片

由这张图可以看到有四个集群,分别是 NameServer 集群、Broker 集群、Producer 集群和 Consumer 集群:

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

2.Broker: 通过提供轻量级的 Topic 和 Queue 机制来处理消息存储,同时支持推(push)和拉(pull)模式以及主从结构的容错机制。

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

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

简单说明一下图中箭头含义,从 Broker 开始,Broker Master1 和 Broker Slave1 是主从结构,它们之间会进行数据同步,即 Date Sync。同时每个 Broker 与
NameServer 集群中的所有节
点建立长连接,定时注册 Topic 信息到所有 NameServer 中。

Producer 与 NameServer 集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Broker Master 建立长连接,且定时向 Broker 发送心跳。Producer 只能将消息发送到 Broker master,但是 Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave
建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。


RocketMQ 集群部署模式

1.单 master 模式
也就是只有一个 master 节点,称不上是集群,一旦这个 master 节点宕机,那么整个服务就不可用,适合个人学习使用。

2.多 master 模式
多个 master 节点组成集群,单个 master 节点宕机或者重启对应用没有影响。
优点:所有模式中性能最高
缺点:单个 master 节点宕机期间,未被消费的消息在节点恢复之前不可用,消息的实时性就受到影响。
注意:使用同步刷盘可以保证消息不丢失,同时 Topic 相对应的 queue 应该分布在集群中各个节点,而不是只在某各节点上,否则,该节点宕机会对订阅该 topic 的应用造成影响。

3.多 master 多 slave 异步复制模式
在多 master 模式的基础上,每个 master 节点都有至少一个对应的 slave。master
节点可读可写,但是 slave 只能读不能写,类似于 mysql 的主备模式。
优点: 在 master 宕机时,消费者可以从 slave 读取消息,消息的实时性不会受影响,性能几乎和多 master 一样。
缺点:使用异步复制的同步方式有可能会有消息丢失的问题。

4.多 master 多 slave 同步双写模式
同多 master 多 slave 异步复制模式类似,区别在于 master 和 slave 之间的数据同步方式。
优点:同步双写的同步模式能保证数据不丢失。
缺点:发送单个消息 RT 会略长,性能相比异步复制低10%左右。
刷盘策略:同步刷盘和异步刷盘(指的是节点自身数据是同步还是异步存储)
同步方式:同步双写和异步复制(指的一组 master 和 slave 之间数据的同步)
注意:要保证数据可靠,需采用同步刷盘和同步双写的方式,但性能会较其他方式低


RocketMQ 单主实战(Mac)

1.本机搭建RocketMQ服务

安装条件

64位操作系统,建议使用 Linux / Unix / Mac;

64位JDK 1.8+;

Maven 3.2.x

下载和构建

从 https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.2.0/rocketmq-all-4.2.0-source-release.zip 下载 4.2.0 的源码版本,执行以下命令来解压4.2.0源码版本并构建二进制文件。

unzip rocketmq-all-4.2.0-source-release.zip

cd rocketmq-all-4.2.0/

mvn -Prelease-all -DskipTests clean install -U

构建成功后,进入到目录并启动NameServer和Broker

//进入到目录
cd distribution/target/apache-rocketmq

//启动NameServer
nohup sh bin/mqnamesrv &
//查看NameServer Log
tail -f ~/logs/rocketmqlogs/namesrv.log

//启动Broke
nohup sh bin/mqbroker -n localhost:9876 &
//查看Broke Log
tail -f ~/logs/rocketmqlogs/broker.log

新建SpringBoot项目

在pom.xml中添加依赖

Spring Boot 中使用 RocketMQ_第2张图片

配置文件application.properties

Spring Boot 中使用 RocketMQ_第3张图片

主函数

@SpringBootApplication
public class SpringBootRocketmqApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootRocketmqApplication.class, args);
	}
}

消息生产者

package com.peter.springboot.rocketmq;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * 描述: 消息生产者
 *
 * @author Peter Pan
 * @create 2018-12-01 18:09
 **/
@Component
public class Producer {

    /**
     * 生产者的组名
     */
    @Value("${apache.rocketmq.producer.producerGroup}")
    private String producerGroup;

    /**
     * NameServer 地址
     */
    @Value("${apache.rocketmq.namesrvAddr}")
    private String namesrvAddr;

    @PostConstruct
    public void defaultMQProducer() {

        //生产者的组名
        DefaultMQProducer producer = new DefaultMQProducer(producerGroup);

        //指定NameServer地址,多个地址以 ; 隔开
        producer.setNamesrvAddr(namesrvAddr);

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

            for (int i = 0; i < 100; i++) {
                String messageBody = "发送给RocketMQ消息内容:" + i;
                String message = new String(messageBody.getBytes(), "utf-8");

                //构建消息
                Message msg = new Message("PushTopic" /* PushTopic */, "push"/* Tag  */, "key_" + i /* Keys */, message.getBytes());
                //发送消息
                SendResult result = producer.send(msg);

                System.out.println("发送响应:MsgId:" + result.getMsgId() + ",发送状态:" + result.getSendStatus());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            producer.shutdown();
        }

    }
}

消息消费者

package com.peter.springboot.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.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * 描述: 消息消费者
 *
 * @author Peter Pan
 * @create 2018-12-01 18:11
 **/
@Component
public class Consumer {

    /**
     * 消费者的组名
     */
    @Value("${apache.rocketmq.consumer.PushConsumer}")
    private String consumerGroup;

    /**
     * NameServer地址
     */
    @Value("${apache.rocketmq.namesrvAddr}")
    private String namesrvAddr;

    @PostConstruct
    public void defaultMQPushConsumer() {

        //消费者的组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);

        //指定NameServer地址,多个地址以 ; 隔开
        consumer.setNamesrvAddr(namesrvAddr);
        try {
            //订阅PushTopic下Tag为push的消息
            consumer.subscribe("PushTopic", "push");

            //设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费
            //如果非第一次启动,那么按照上次消费的位置继续消费
            consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
            consumer.registerMessageListener(new MessageListenerConcurrently() {

                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext context) {
                    try {
                        for (MessageExt messageExt : list) {
                            System.out.println("messageExt: " + messageExt);//输出消息内容
                            String messageBody = new String(messageExt.getBody(), "utf-8");
                            System.out.println("RocketMQ消费响应:Msg: " + messageExt.getMsgId() + ",msgBody: " + messageBody);//输出消息内容
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        return ConsumeConcurrentlyStatus.RECONSUME_LATER; //稍后再试
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; //消费成功
                }
            });
            consumer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

启动服务后

Spring Boot 中使用 RocketMQ_第4张图片

监控服务 RocketMQ-Console-Ng

1.gitclone rocketmq-externals 项目 https://github.com/apache/rocketmq-externals

2.更改配置

3.编译并启动

编译成功后可以在target目录下看到生成jar,启动RocketMQ-Console-Ng

mvn clean package -Dmaven.test.skip=true

cd /target

java -jar rocketmq-console-ng-1.0.0.jar

访问 http://localhost:8080/

Spring Boot 中使用 RocketMQ_第5张图片

Spring Boot 中使用 RocketMQ_第6张图片 

Spring Boot 中使用 RocketMQ_第7张图片

Spring Boot 中使用 RocketMQ_第8张图片 

Spring Boot 中使用 RocketMQ_第9张图片 

Spring Boot 中使用 RocketMQ_第10张图片 

Spring Boot 中使用 RocketMQ_第11张图片 

你可能感兴趣的:(SpringBoot,&,SpringCloud,学习实战)