1 RocketMQ简介与核心概念
1.1 什么是RocketMQ?
RocketMQ是由阿里巴巴开源的分布式消息中间件,支持亿级并发、百万级堆积的消息处理能力。它具有高可用、高性能、灵活扩展的特性,广泛应用于大数据实时计算、日志收集、分布式事务处理等场景。作为Apache顶级项目,RocketMQ在阿里内部承接了"双11"等高并发场景的消息流转,能够处理万亿级别的消息。
1.2 核心架构与组件
理解RocketMQ的架构是正确使用它的基础,其主要包含以下几个核心组件:
- NameServer:无状态路由中心,负责Broker的注册与发现。与ZooKeeper不同,NameServer无强一致性要求,各节点间互不通信,提高了简单性和性能。
- Broker:消息存储与转发节点,支持主从架构实现高可用。Broker负责消息的存储、投递和查询,并保证消息的可靠性。
- Producer:消息生产者,负责发送消息。Producer从NameServer获取Broker路由信息,然后将消息发送到合适的Broker。
- Consumer:消息消费者,订阅并消费消息。Consumer同样从NameServer获取路由信息,然后从Broker拉取消息进行消费。
1.3 消息模型核心概念
- Topic(主题):消息的分类标识,用于区分不同类别的消息。生产者发送消息到指定Topic,消费者订阅指定Topic来接收消息。
- Message Queue(队列):Topic的实际存储单元,每个Topic包含多个Queue,消息实际存储在Queue中。Queue是并行生产和消费的最小单元,支持分布式存储与负载均衡。
- Tag(标签):Topic的二级分类,用于进一步细化消息分类,方便消费者进行过滤订阅。
- Consumer Group(消费组):由多个消费者实例组成的集合,同一消费组内的消费者共同消费同一Topic的消息,实现负载均衡。
- Message(消息):通信的基本单位,包含消息体(Body)、Topic、Tag、Key等属性。
表:RocketMQ核心组件功能总结
组件 | 角色定位 | 关键特性 |
---|---|---|
NameServer | 路由注册中心 | 无状态、轻量级、最终一致性 |
Broker | 消息存储服务 | 高可用、数据持久化、主从复制 |
Producer | 消息生产者 | 多种发送模式、负载均衡 |
Consumer | 消息消费者 | 集群/广播模式、推/拉消费 |
2 环境搭建与配置
2.1 安装RocketMQ
以下是基于Linux环境的安装步骤:
- 下载与解压:
bash
wget https://archive.apache.org/dist/rocketmq/4.9.3/rocketmq-all-4.9.3-bin-release.zip
unzip rocketmq-all-4.9.3-bin-release.zip -d /usr/local
- 配置环境变量:
bash
export ROCKETMQ_HOME=/usr/local/rocketmq-all-4.9.3
export PATH=$PATH:$ROCKETMQ_HOME/bin
- 调整JVM内存 (避免内存不足): 编辑
bin/runbroker.sh
和bin/runserver.sh
,修改JVM参数:
bash
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m"
2.2 启动与停止服务
- 启动NameServer:
bash
nohup sh bin/mqnamesrv &
tail -f ~/logs/rocketmqlogs/namesrv.log
- 启动Broker:
bash
nohup sh bin/mqbroker -n localhost:9876 &
tail -f ~/logs/rocketmqlogs/broker.log
- 服务停止:
bash
sh bin/mqshutdown namesrv
sh bin/mqshutdown broker
2.3 关键配置详解
Broker的核心配置文件conf/broker.conf
常见配置项:
properties
# 集群名称
brokerClusterName=DefaultCluster
# broker名称,主从节点使用不同名称
brokerName=broker-a
# 0表示Master,>0表示Slave
brokerId=0
# NameServer地址
namesrvAddr=127.0.0.1:9876
# 存储路径
storePathRootDir=/usr/local/rocketmq/store
# 刷盘方式:ASYNC_FLUSH(异步,性能高)或SYNC_FLUSH(同步,可靠)
flushDiskType=ASYNC_FLUSH
# Broker角色:ASYNC_MASTER、SYNC_MASTER、SLAVE
brokerRole=ASYNC_MASTER
3 基础消息收发实战
3.1 添加Maven依赖
xml
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.9.3</version>
</dependency>
3.2 消息生产者示例
java
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
public class SimpleProducer {
public static void main(String[] args) throws Exception {
// 1. 创建生产者实例,指定生产者组名
DefaultMQProducer producer = new DefaultMQProducer("producer_group");
// 2. 指定NameServer地址
producer.setNamesrvAddr("localhost:9876");
// 3. 设置同步发送失败时的重试次数,默认为2
producer.setRetryTimesWhenSendFailed(2);
// 4. 启动Producer实例
producer.start();
// 5. 创建消息,指定Topic、Tag和消息体
Message msg = new Message("TopicTest",
"TagA",
"Hello RocketMQ".getBytes("UTF-8"));
// 可选:设置业务键,用于消息追踪
msg.setKeys("ORDER_12345");
// 6. 发送消息
SendResult sendResult = producer.send(msg);
System.out.printf("发送结果:%s%n", sendResult);
// 7. 关闭生产者
producer.shutdown();
}
}
3.3 消息消费者示例
java
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
public class SimpleConsumer {
public static void main(String[] args) throws Exception {
// 1. 创建消费者实例,指定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group");
// 2. 指定NameServer地址
consumer.setNamesrvAddr("localhost:9876");
// 3. 订阅Topic和Tag(*表示所有Tag)
consumer.subscribe("TopicTest", "*");
// 4. 设置消费模式:集群模式(默认)或广播模式
// consumer.setMessageModel(MessageModel.BROADCASTING);
// 5. 注册消息监听器处理消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
String topic = msg.getTopic();
String tags = msg.getTags();
String body = new String(msg.getBody());
System.out.printf("收到消息:Topic=%s, Tag=%s, Body=%s%n", topic, tags, body);
}
// 返回消费成功状态
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 6. 启动消费者
consumer.start();
System.out.println("消费者已启动,等待消息...");
}
}
3.4 消息发送方式对比
RocketMQ提供三种消息发送模式,适用于不同场景:
- 同步发送:发送后阻塞等待Broker响应,可靠性最高但性能较低。
java
SendResult result = producer.send(msg);
- 异步发送:发送后立即返回,通过回调函数处理结果,性能高。
java
producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("发送成功:" + sendResult);
}
@Override
public void onException(Throwable e) {
System.out.println("发送失败:" + e.getMessage());
}
});
- 单向发送:只发送不应答,适用于日志收集等允许消息丢失的场景。
java
producer.sendOneway(msg);
表:消息发送方式对比
发送方式 | 可靠性 | 响应时间 | 适用场景 |
---|---|---|---|
同步发送 | 高 | 高 | 重要通知、短信等强一致性场景 |
异步发送 | 高 | 中 | 响应敏感业务,如用户下单 |
单向发送 | 低 | 低 | 日志收集、 metrics数据等 |
4 RocketMQ高级特性详解
4.1 顺序消息
顺序消息确保同一业务键的消息按照发送顺序被消费,例如订单的创建、付款、发货流程必须有序处理。
生产者实现:
java
// 使用MessageQueueSelector确保同一业务ID的消息发送到同一队列
public class OrderedProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("ordered_producer_group");
producer.start();
for (int i = 0; i < 10; i++) {
Message msg = new Message("OrderTopic", "TagA",
("ORDER_" + i).getBytes());
// 使用订单ID作为选择器参数,确保同一订单发送到同一队列
SendResult result = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Integer orderId = (Integer) arg;
int index = orderId % mqs.size();
return mqs.get(index);
}
}, i); // i作为订单ID
System.out.printf("顺序消息发送结果:%s%n", result);
}
producer.shutdown();
}
}
消费者实现:
java
public class OrderedConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ordered_consumer_group");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("OrderTopic", "*");
// 注册顺序消息监听器
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeOrderlyContext context) {
for (MessageExt msg : msgs) {
System.out.printf("顺序消费:消息内容=%s%n", new String(msg.getBody()));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.println("顺序消费者已启动");
}
}
4.2 事务消息
事务消息确保本地事务与消息发送的原子性,适用于分布式事务场景。
java
public class TransactionProducer {
public static void main(String[] args) throws Exception {
// 创建事务生产者
TransactionMQProducer producer = new TransactionMQProducer("transaction_producer_group");
producer.setNamesrvAddr("localhost:9876");
// 设置事务监听器
producer.setTransactionListener(new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// 执行本地事务
try {
// 模拟本地数据库操作
boolean success = doLocalTransaction();
return success ? LocalTransactionState.COMMIT_MESSAGE :
LocalTransactionState.ROLLBACK_MESSAGE;
} catch (Exception e) {
return LocalTransactionState.UNKNOW;
}
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// Broker回调检查本地事务状态
return checkTransactionStatus(msg.getTransactionId()) ?
LocalTransactionState.COMMIT_MESSAGE :
LocalTransactionState.ROLLBACK_MESSAGE;
}
});
producer.start();
// 发送事务消息
Message msg = new Message("TransactionTopic", "TagA",
"事务消息内容".getBytes());
SendResult result = producer.sendMessageInTransaction(msg, null);
System.out.printf("事务消息发送结果:%s%n", result);
producer.shutdown();
}
}
4.3 延迟消息
延迟消息在指定延迟时间后才可被消费,适用于定时任务、超时处理等场景。
java
public class DelayedProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("delayed_producer_group");
producer.start();
Message msg = new Message("DelayedTopic", "TagA",
"延迟消息".getBytes());
// 设置延迟级别:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
msg.setDelayTimeLevel(3); // 对应10秒延迟
SendResult result = producer.send(msg);
System.out.printf("延迟消息发送结果:%s,延迟级别:%d%n",
result, msg.getDelayTimeLevel());
producer.shutdown();
}
}
4.4 消息过滤
RocketMQ支持基于Tag和SQL92语法的消息过滤。
Tag过滤:
java
// 生产者发送带Tag的消息
Message msg = new Message("FilterTopic", "ImportantTag",
"重要消息".getBytes());
// 消费者只订阅ImportantTag的消息
consumer.subscribe("FilterTopic", "ImportantTag");
SQL过滤:
java
// 生产者设置消息属性
msg.putUserProperty("priority", "high");
msg.putUserProperty("type", "business");
// 消费者使用SQL表达式过滤
consumer.subscribe("SQLFilterTopic",
MessageSelector.bySql("priority = 'high' AND type = 'business'"));
5 Spring Boot集成实战
5.1 添加Spring Boot Starter依赖
xml
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
5.2 配置参数
yaml
# application.yml
rocketmq:
name-server: localhost:9876
producer:
group: spring-boot-producer-group
send-message-timeout: 3000
5.3 Spring Boot生产者
java
@Component
public class SpringBootProducer {
@Autowired
private RocketMQTemplate rocketMQTemplate;
public void sendMessage() {
// 发送简单消息
rocketMQTemplate.convertAndSend("SpringBootTopic", "Hello Spring Boot RocketMQ");
// 发送带Tag的消息
Message<String> message = MessageBuilder.withPayload("带Tag的消息")
.setHeader(RocketMQHeaders.TAGS, "TagA")
.build();
rocketMQTemplate.send("SpringBootTopic", message);
// 发送顺序消息
rocketMQTemplate.syncSendOrderly("OrderTopic", "顺序消息", "order123");
}
}
5.4 Spring Boot消费者
java
@Component
@RocketMQMessageListener(
topic = "SpringBootTopic",
consumerGroup = "spring-boot-consumer-group",
selectorExpression = "TagA" // 过滤Tag为TagA的消息
)
public class SpringBootConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("接收到消息:" + message);
// 处理业务逻辑
}
}
6 实战案例:电商订单系统
下面通过一个完整的电商订单案例展示RocketMQ在实际项目中的应用。
6.1 系统架构
- 用户下单 → 订单服务 → 订单Topic → 库存服务、物流服务、通知服务
6.2 订单消息生产者
java
@Service
public class OrderService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
public void createOrder(Order order) {
// 1. 保存订单到数据库
boolean saveSuccess = orderDao.save(order);
if (saveSuccess) {
// 2. 发送订单创建消息
String topic = "OrderTopic";
String tags = "CreateOrder";
String messageBody = buildOrderMessage(order);
Message<String> message = MessageBuilder.withPayload(messageBody)
.setHeader(RocketMQHeaders.TAGS, tags)
.setHeader(RocketMQHeaders.KEYS, order.getOrderNo())
.build();
// 同步发送重要消息,确保订单创建通知送达
SendResult result = rocketMQTemplate.syncSend(topic, message);
// 3. 发送延迟消息,检查订单支付超时(30分钟未支付自动取消)
if (result.getSendStatus() == SendStatus.SEND_OK) {
Message<String> delayMessage = MessageBuilder.withPayload(order.getOrderNo())
.setHeader(RocketMQHeaders.TAGS, "OrderTimeout")
.build();
// 延迟级别16对应30分钟
rocketMQTemplate.syncSend(topic, delayMessage, 3000, 16);
}
}
}
private String buildOrderMessage(Order order) {
JSONObject message = new JSONObject();
message.put("orderNo", order.getOrderNo());
message.put("userId", order.getUserId());
message.put("amount", order.getAmount());
message.put("createTime", order.getCreateTime());
return message.toJSONString();
}
}
6.3 库存消费者
java
@Component
@RocketMQMessageListener(
topic = "OrderTopic",
consumerGroup = "inventory-consumer-group",
selectorExpression = "CreateOrder"
)
public class InventoryService implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
try {
// 解析订单消息
JSONObject orderInfo = JSON.parseObject(message);
String orderNo = orderInfo.getString("orderNo");
// 扣减库存
boolean deductSuccess = inventoryDao.deductStock(orderInfo);
if (deductSuccess) {
System.out.println("库存扣减成功,订单号:" + orderNo);
// 发送库存扣减成功消息
rocketMQTemplate.convertAndSend("InventoryTopic", "库存扣减成功:" + orderNo);
} else {
System.out.println("库存扣减失败,订单号:" + orderNo);
}
} catch (Exception e) {
// 记录日志并重试
log.error("库存服务处理消息失败:{}", message, e);
throw new RuntimeException("处理失败,触发重试机制");
}
}
}
6.4 订单超时检查消费者
java
@Component
@RocketMQMessageListener(
topic = "OrderTopic",
consumerGroup = "order-timeout-consumer-group",
selectorExpression = "OrderTimeout"
)
public class OrderTimeoutService implements RocketMQListener<String> {
@Override
public void onMessage(String orderNo) {
// 检查订单支付状态
Order order = orderDao.getByOrderNo(orderNo);
if (order != null && order.getStatus() == OrderStatus.UNPAID) {
// 超时未支付,自动取消订单
order.setStatus(OrderStatus.CANCELLED);
orderDao.update(order);
// 恢复库存
rocketMQTemplate.convertAndSend("InventoryTopic",
new InventoryMessage(orderNo, InventoryAction.RESTORE));
System.out.println("订单超时取消:" + orderNo);
}
}
}
7 运维监控与问题排查
7.1 可视化监控平台
RocketMQ提供Web控制台进行集群监控:
- 下载并编译控制台:
bash
git clone https://github.com/apache/rocketmq-dashboard.git
cd rocketmq-dashboard
mvn clean package -Dmaven.test.skip=true
- 启动控制台:
bash
nohup java -jar rocketmq-dashboard-2.0.0.jar > tmp.log &
7.2 常见问题与解决方案
1. 消息发送失败
- 问题 :
MQClientException: The broker not found
- 解决:检查NameServer和Broker是否启动,网络连接是否正常
2. 消息堆积处理
- 临时方案:增加Consumer实例数量,提高消费能力
- 根本解决:优化消费逻辑,避免阻塞操作
3. 消息重复消费
- 解决方案:实现消费幂等性
java
// 基于数据库唯一键实现幂等消费
public class IdempotentConsumer {
public void processMessage(Message msg) {
String msgId = msg.getMsgId();
String businessId = msg.getKeys();
// 检查消息是否已处理
if (messageLogDao.isProcessed(businessId)) {
return; // 已处理,直接返回
}
// 处理业务逻辑
processBusiness(msg);
// 记录已处理消息
messageLogDao.markProcessed(businessId, msgId);
}
}
7.3 性能优化建议
-
生产者优化:
- 使用异步发送提高吞吐量
- 批量发送小消息(注意批量消息大小限制)
-
消费者优化:
- 提高并发消费线程数(
consumer.setConsumeThreadMin(20)
) - 批量消费消息
- 提高并发消费线程数(
-
Broker优化:
- 根据场景选择刷盘策略(异步刷盘性能高,同步刷盘可靠性高)
- 合理设置内存参数,避免频繁GC
8 总结
RocketMQ作为一款成熟的分布式消息中间件,为分布式系统提供了可靠的消息通信能力。通过本文的全面介绍,你应该已经掌握了:
- 核心概念:理解Topic、Queue、Producer/Consumer等核心组件
- 环境搭建:能够独立部署RocketMQ集群
- API使用:掌握同步、异步、顺序、事务等消息的发送与消费
- 高级特性:熟练运用延迟消息、消息过滤等高级功能
- 实战能力:能够在实际项目中设计和实现基于消息队列的解决方案
在实际项目中使用RocketMQ时,切记要根据业务场景选择合适的消息类型和发送方式,同时重视消息的可靠性和幂等性处理,这样才能构建出稳定可靠的分布式系统。
希望这篇全面的指南能够帮助你在实际项目中更好地使用RocketMQ!