Java随笔 | 消息队列RocketMQ的简介、安装与使用(非Springboot项目)

文章目录

  • 一、什么是消息队列,它解决了什么问题?
  • 二、在Linux中安装消息队列
    • 2.1 官网下载
    • 2.2 上传到Linux服务器并解压缩
    • 2.3 (可选)修改配置文件中的默认内存
    • 2.4 放行防火墙端口
    • 2.5 运行mqnamesrv和mqbroker
    • 2.6 Linux端测试消息收发
  • 三、普通Java项目中实现消息的简单收发
    • 3.1 Linux中创建Topic
    • 3.2 引入依赖
    • 3.3 创建消息生产者Producer
    • 3.4 创建消息消费者Consumer
  • 四、三种发送方式与消息过滤
    • 4.1 概述
    • 4.2 同步发送
    • 4.3 异步发送
    • 4.4 单向模式发送
    • 4.5 消息过滤


一、什么是消息队列,它解决了什么问题?

系统之间传递消息,传统方式是系统A直接发消息给系统B,而消息队列则是在二者之间作为“中间件”的作用,其最大的好处在于实现了消息收发的解耦、异步、削峰

详细了解传送门(写得深入浅出 非常推荐):什么是消息队列

通过这种在二者加入中间件提升系统整体效率的例子,最典型的就是用货币这个媒介替代传统的以物易物。

目前主流的消息队列有:

  • rabbitmq、zeromq、activemq:传统、老牌、稳定、通用
  • rocketmq:阿里开源,通用、性能高、新、功能强
  • kafka、pulsar:快、新

本文使用的是阿里开源的rocketmq作为案例介绍

二、在Linux中安装消息队列

2.1 官网下载

首先前往官网下载:https://rocketmq.apache.org/zh/download/

下载最新或最近版本即可,本文下载的版本为4.9.3
Java随笔 | 消息队列RocketMQ的简介、安装与使用(非Springboot项目)_第1张图片

2.2 上传到Linux服务器并解压缩

下载完成后,将zip压缩包上传到Linux服务器,并使用unzip命令解压缩。

# 如果提示没有unzip命令 先用yum安装即可
yum install -y unzip zip

# 解压缩到当前文件夹
unzip rocketmq-all-4.9.3-bin-release.zip

# 将解压后的目录移动到根目录的opt目录下 该目录约定俗成用于存放第三方软件
mv rocketmq-4.9.3 /opt/

2.3 (可选)修改配置文件中的默认内存

# 进入配置文件所在目录
cd /opt/rocketmq-4.9.3/bin

# 修改runbroker.sh 进入后输入 :set nu 显示行号
vim runbroker.sh
# 修改第85行 值供参考
85 JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m"

# 修改runserver.sh
vim runserver.sh
# 修改第71行 值供参考
71 JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn64m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=80m"

2.4 放行防火墙端口

需要放行4个端口,9876,10911,10912,10909。
参考:RocketMQ服务中各端口号说明

# 放行4个端口
firewall-cmd --add-port=9876/tcp --zone=public --permanent
firewall-cmd --add-port=10909/tcp --zone=public --permanent
firewall-cmd --add-port=10911/tcp --zone=public --permanent
firewall-cmd --add-port=10912/tcp --zone=public --permanent

# 放行后需要重新加载防火墙配置
firewall-cmd --reload

2.5 运行mqnamesrv和mqbroker

# 确保当前处于rocketmq的根目录
cd /opt/rocketmq-4.9.3

# 运行mqnamesrv
nohup sh bin/mqnamesrv &

# 运行broker
nohup sh bin/mqbroker -n localhost:9876 &

# 查看日志输出
tailf nohup.out

2.6 Linux端测试消息收发

在进行工具测试消息收发之前,我们需要告诉客户端NameServer的地址,这里我们使用环境变量NAMESRV_ADDR告诉客户端地址。

# 测试消息发送
export NAMESRV_ADDR=localhost:9876  # 配置环境变量
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer

# 测试消息接收
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer

三、普通Java项目中实现消息的简单收发

  • 参考:快速开始 | RocketMQ (apache.org)

3.1 Linux中创建Topic

  • 参考:RocketMq mqadmin 的用法详解
# 确保目录在mq的根目录下(以下两行命令任选一)
sh bin/mqadmin updatetopic -n localhost:9876 -t TestTopic -c DefaultCluster
# 或者如下命令 -b broker地址 表示topic建在该broker
sh bin/mqadmin updatetopic -n localhost:9876 -b localhost:10911 -t TestTopic

3.2 引入依赖

在java中实现消息收发,先在pom.xml中引入依赖。


<dependency>
    <groupId>org.apache.rocketmqgroupId>
    <artifactId>rocketmq-client-javaartifactId>
    <version>4.9.3version>
dependency> 

3.3 创建消息生产者Producer

新建一个类:Producer

// 创建消息生产者Producer
public class Producer {
    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
        // 1.创建并启动生产者
        DefaultMQProducer producer = new DefaultMQProducer("simple_producer_group");// 任取一消息群组名
        producer.setNamesrvAddr("192.168.xxx.xxx:9876");// 设置端口和ip
        producer.start();// 启动producer
        
        // 2.准备消息对象Message并发送
        String content = "hello";// 要发送的内容
        Message message = new Message("TestTopic", content.getBytes(StandardCharsets.UTF_8));// 创建消息对象 设置topic名
        SendResult result = producer.send(message);// 发送消息
        System.out.println(result);// 输出发送的结果
        
        // 3.关闭producer
        producer.shutdown();
    }
}

3.4 创建消息消费者Consumer

新建一个类:Consumer

// 创建消息消费者Consumer
public class Consumer {
    public static void main(String[] args) throws MQClientException {
        // 1.创建消息消费者
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("simple_consumer_group");// 群组名与生产者一致
        consumer.setNamesrvAddr("192.168.xxx.xxx:9876");// 与生产者一致

        // 2.订阅特定的topic *表示所有
        consumer.subscribe("TestTopic", "*");

        // 3.设置消息监听器
        consumer.registerMessageListener((MessageListenerConcurrently) (msgs, consumeConcurrentlyContext) -> {
            for (MessageExt ext : msgs)
                System.out.println("收到消息:"+new String(ext.getBody(), StandardCharsets.UTF_8));
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });
        
        // 4.启动消息消费者
        consumer.start();
    }
}

四、三种发送方式与消息过滤

4.1 概述

消息队列发送消息有3种方式,分别是同步发送、异步发送和单向模式发送。

  • 参考:普通消息发送 | RocketMQ (apache.org)

同步发送:是最常用的方式,是指消息发送方发出一条消息后,会在收到服务端同步响应之后才发下一条消息的通讯方式,可靠的同步传输被广泛应用于各种场景,如重要的通知消息、短消息通知等。
Java随笔 | 消息队列RocketMQ的简介、安装与使用(非Springboot项目)_第2张图片
异步发送:发送方发出一条消息后,不等服务端返回响应,接着发送下一条消息的通讯方式。
Java随笔 | 消息队列RocketMQ的简介、安装与使用(非Springboot项目)_第3张图片
单向模式发送:发送方只负责发送消息,不等待服务端返回响应且没有回调函数触发,即只发送请求不等待应答。此方式发送消息的过程耗时非常短,一般在微秒级别。适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集。
Java随笔 | 消息队列RocketMQ的简介、安装与使用(非Springboot项目)_第4张图片
消息过滤:在发送消息时带上tag便签,在消费端只接收指定tag的消息。

4.2 同步发送

同步发送的整个代码流程如下:

  1. 首先会创建一个producer。普通消息可以创建 DefaultMQProducer,创建时需要填写生产组的名称,生产者组是指同一类Producer的集合,这类Producer发送同一类消息且发送逻辑一致。
  2. 设置NameServer的地址。Apache RocketMQ很多方式设置NameServer地址(客户端配置中有介绍),这里是在代码中调用producer的API setNamesrvAddr进行设置,如果有多个NameServer,中间以分号隔开,比如"127.0.0.2:9876;127.0.0.3:9876"。
  3. 第三步是构建消息。指定topic、tag、body等信息,tag可以理解成标签,对消息进行再归类,RocketMQ可以在消费端对tag进行过滤。
  4. 最后调用send接口将消息发送出去。同步发送等待结果最后返回SendResult,SendResut包含实际发送状态还包括SEND_OK(发送成功), FLUSH_DISK_TIMEOUT(刷盘超时), FLUSH_SLAVE_TIMEOUT(同步到备超时), SLAVE_NOT_AVAILABLE(备不可用),如果发送失败会抛出异常。
  5. :同步发送方式请务必捕获发送异常,并做业务侧失败兜底逻辑,如果忽略异常则可能会导致消息未成功发送的情况。
// 同步发送 - 消息生产者(消费者代码不变 参考上文)
public class ProducerDemo {
    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
        // 1.创建并启动producer 消息生产者
        DefaultMQProducer producer = new DefaultMQProducer("simple_producer_group");
        producer.setNamesrvAddr("192.168.xxx.xxx:9876");
        producer.start();

		// 2.消息的内容
        String content = "hello";
		
        // 3.创建并发送消息(同步)
        Message message = new Message("TestTopic", content.getBytes(StandardCharsets.UTF_8));
        SendResult result = producer.send(message);
        System.out.println(result);// 输出返回结果
        
        // 4.producer不再使用 关闭producer
        producer.shutdown();
    }
}

4.3 异步发送

异步发送需要实现异步发送回调接口(SendCallback)。

消息发送方在发送了一条消息后,不需要等待服务端响应即可发送第二条消息,发送方通过回调接口接收服务端响应,并处理响应结果。异步发送一般用于链路耗时较长,对响应时间较为敏感的业务场景。例如,视频上传后通知启动转码服务,转码完成后通知推送转码结果等。

异步发送与同步发送代码唯一区别在于调用send接口的参数不同,异步发送不会等待发送返回,取而代之的是send方法需要传入 SendCallback 的实现,SendCallback 接口主要有onSuccess 和 onException 两个方法,表示消息发送成功和消息发送失败。

如下是示例代码。

// 异步发送 - 消息生产者(消费者代码不变 参考上文)
public class ProducerDemo {
    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException {
        // 1.创建并启动producer 消息生产者
        DefaultMQProducer producer = new DefaultMQProducer("simple_producer_group");
        producer.setNamesrvAddr("192.168.xxx.xxx:9876");
        producer.start();

		// 2.消息的内容
        String content = "hello";

        // 3.创建并发送消息(异步)
        for (int i = 0; i < 10; i++) {
            Message message = new Message("TestTopic", (content + i).getBytes(StandardCharsets.UTF_8));
            producer.send(message, new SendCallback() {
                @Override// 返回成功时的操作
                public void onSuccess(SendResult sendResult) {
                    System.out.println(sendResult);
                }
                @Override// 返回失败时的操作
                public void onException(Throwable e) {
                    System.out.println(e.getMessage());
                }
            });
        }
        
		// 4.producer不再使用 关闭producer
        producer.shutdown();
    }
}

4.4 单向模式发送

消息发送者只负责发,不考虑响应返回的问题。单向模式调用sendOneway,不会对返回结果有任何等待和处理。

以下是示例代码。

// 单向模式发送 - 消息生产者(消费者代码不变 参考上文)
public class ProducerDemo {
    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException {
        // 1.创建并启动producer 消息生产者
        DefaultMQProducer producer = new DefaultMQProducer("simple_producer_group");
        producer.setNamesrvAddr("192.168.xxx.xxx:9876");
        producer.start();

		// 2.消息的内容
        String content = "hello";

        // 3.单向模式发送
        Message message = new Message("TestTopic", content.getBytes(StandardCharsets.UTF_8));
        producer.sendOneway(message);

		// 4.producer不再使用 关闭producer
        producer.shutdown();
    }
}

4.5 消息过滤

消息生产者在发送Message时增加tag参数,即带上标签,消息消费者可订阅特定的标签。

// 消息生产者
public class ProducerDemo {
    public static void main(String[] args) {
        // 1.创建并启动producer 消息生产者
		...

		// 2.消息的内容
		...

        // 3.发送消息(使用tag)
        String tag = "good";// 设置tag
        Message message = new Message("TestTopic", tag, content.getBytes(StandardCharsets.UTF_8));// 带上tag
        producer.sendOneway(message);// 发送消息

		// 4.producer不再使用 关闭producer
        ...
    }
}
// 消息消费者(体现在第2步)
public class ConsumerDemo {

    public static void main(String[] args) throws MQClientException {
        // 1.创建消息接收者consumer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("simple_consumer_group");
        consumer.setNamesrvAddr("192.168.xxx.xxx:9876");

        // 2.订阅特定的topic(tag) *表示订阅所有
        consumer.subscribe("TestTopic", "good");

        // 3.消息监听器
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for (MessageExt ext : msgs){
                    System.out.println("收到消息:"+new String(ext.getBody(), StandardCharsets.UTF_8));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        
		// 4.启动消息消费者
        consumer.start();
    }
}

关于消息队列在springboot项目中的使用,详见我后续的文章。

你可能感兴趣的:(Java随笔,java,java-rocketmq,rocketmq)