1. 前言
1.1 什么是消息队列
消息队列是一种在应用程序之间传递消息的通信模式。它可以解耦发送者和接收者之间的耦合关系,使得应用程序可以异步、可靠地进行通信。消息队列中的消息被发送到一个中间件或消息代理,然后由接收者从中间件中获取和处理。
消息队列的使用场景有很多,例如:
-
异步通信:发送者将消息发送到队列中,然后可以继续执行其他任务,而不需要等待接收者立即处理消息。
-
事件驱动:许多系统都使用事件驱动的架构,其中各个组件通过发送和接收消息来进行通信。
-
流量削峰:当系统面临突然增加的请求量时,可以使用消息队列来平滑处理请求,避免系统崩溃或过载。
解耦系统组件:通过使用消息队列,系统的不同组件可以独立地开发和部署,减少了它们之间的依赖性。
一些常见的消息队列系统包括RabbitMQ、Apache Kafka、ActiveMQ、RocketMQ等。它们提供了可靠的消息传递、消息持久化、消息分发等功能,以满足不同应用场景的需求。
1.2 什么是RocketMQ
1.2.1 介绍
Apache RocketMQ 是一款低延迟、高并发、高可用、高可靠的分布式消息中间件。消息队列 RocketMQ 可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。
1.2.2 概念
- Topic:消息主题,用于将一类的消息进行归类,比如订单主题,就是所有订单相关的消息都可以由这个主题去承载,生产者向这个主题发送消息。
- 生产者:负责生产消息并发送消息到 Topic 的角色。
- 消费者:负责从 Topic 接收并消费消息的角色。
- 消息:生产者向 Topic 发送的内容,会被消费者消费。
- 消息属性:生产者发送的时候可以为消息自定义一些业务相关的属性,比如 Mesage Key 和 Tag 等。
- Group:一类生产者或消费者,这类生产者或消费者通常生产或消费同一类消息,且消息发布或订阅的逻辑一致。
2 安装及使用
2.1 安装
# 准备nameService挂载盘
mkdir -p /usr/local/docker/rocketMQ/nameService/logs /usr/local/docker/rocketMQ/nameService/store
# 查询rocketmq镜像
docker search rocketmq
# 拉取镜像
docker pull rocketmqinc/rocketmq:4.4.0
# 启动nameService
docker run -d -p 9876:9876 \
-v/usr/local/docker/rocketMQ/nameService/logs:/root/logs \
-v /usr/local/docker/rocketMQ/nameService/store:/root/store \
--name rmqnamesrv -e "MAX_POSSIBLE_HEAP=100000000" rocketmqinc/rocketmq:4.4.0 sh mqnamesrv
# 检查是否启动成功
docker ps # 输出所哟启动中容器
docker logs -f rmqnamesrv # 持续输出容器日志
# 准备broker挂载盘
mkdir -p /usr/local/docker/rocketMQ/broker/logs /usr/local/docker/rocketMQ/broker/store /usr/local/docker/rocketMQ/broker/conf
cd /usr/local/docker/rocketMQ/broker/conf
vim broker.conf
broker.conf ({ip}替换为公网IP)
brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
brokerIP1 = {ip}
# 启动broker
docker run -d -p 10911:10911 -p 10909:10909 \
-v /usr/local/docker/rocketMQ/broker/logs:/root/logs \
-v /usr/local/docker/rocketMQ/broker/store:/root/store \
-v /usr/local/docker/rocketMQ/broker/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
# 检查是否启动成功
docker ps # 输出所哟启动中容器
docker logs -f rmqbroker # 持续输出容器日志
2.2 SpringBoot整合MQ
2.2.1 配置Maven坐标
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.3</version>
</dependency>
2.2.2 配置文件
rocketmq:
nameServer: {IP}:{HOST}
producer:
group: ${spring.application.name}_service_group
{}中内容替换为自己的
若微服务架构内每个服务都引入了MQ, 组名尽量不一致, 这里以服务名称进行区分.
2.2.3 注入使用
//注入
@Autowired
private RocketMQTemplate rocketMQTemplate;
2.2.4 简单案例
注意事项:
- 发送者与消费者Topic必须一致,
- 消费者@RocketMQMessageListener注解中consumerGroup可自定义, 但是不能和配置文件中发送者组一致
2.2.4.1 发送消息
//构建消息体
Message<String> message = MessageBuilder.withPayload("hello world").build();
//发送异步消息
// param1:topic param2:消息体 param:callback
rocketMQTemplate.asyncSend("HELLO_WORLD_TOPIC", message, null);
2.2.4.2 接收消息
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
@Component
@RocketMQMessageListener(topic = "HELLO_WORLD_TOPIC", consumerGroup = "HELLO_WORLD_GROUP")
@Slf4j
public class HelloWorldMessageQueue implements RocketMQListener<String>, RocketMQPushConsumerLifecycleListener {
@Override
public void onMessage(String s) {
}
@Override
public void prepareStart(DefaultMQPushConsumer defaultMQPushConsumer) {
defaultMQPushConsumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
MessageExt messageExt = list.get(0);
byte[] body = messageExt.getBody();
String message = new String(body, StandardCharsets.UTF_8);
System.out.println(message);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
}
}