首先我们来接一下什么是消息队列:消息队列(Message Queue,简称 MQ)是一种跨进程、异步通信的中间件技术 ,用于在分布式系统中解耦生产者与消费者、削峰填谷、保证最终一致性。
使用的场景是:
应用解耦
-
场景:订单服务创建订单后,需通知库存、物流、积分系统。
-
不用 MQ:订单服务直接调用 3 个接口 → 任一系统宕机,订单失败。
-
用 MQ:各系统独立消费,互不影响。
异步处理
-
场景:用户上传头像 → 生成缩略图、更新 CDN、记录审计日志。
-
同步做法:用户等待 3 秒才看到"上传成功"。
-
异步做法 :j
→ 用户体验提升,系统响应更快。
流量削峰(应对高并发)
-
场景:双 11 秒杀,10 万 QPS 瞬间打到数据库。
-
不用 MQ:数据库连接池耗尽,服务雪崩。
-
用 MQ:
-
前端请求只写入 MQ(写速度极快);
-
后端以 1000 QPS 的稳定速度消费;
-
系统不崩溃,只是延迟几秒处理。
-
💡 类似"排队买票":窗口处理不过来,先让人在大厅排队(MQ),避免踩踏(系统崩溃)。
最终一致性(分布式事务)
-
场景 :支付成功后,必须 同时 扣减账户余额 + 增加商品销量。
-
问题:两个操作跨数据库,无法用本地事务。
-
MQ 方案(可靠事件模式):
-
支付服务:
BEGIN; 扣余额; 发送"支付成功"消息; COMMIT; -
商品服务:消费消息 → 增加销量
-
若消息丢失?→ 用 事务消息 (RocketMQ)或 本地消息表 补偿
-
✅ 保证:要么都成功,要么通过重试最终成功。
日志收集 & 大数据 pipeline
-
场景:收集 App 用户点击行为,用于实时分析。
-
架构 :文本
App → Kafka → Flink 实时计算 → 大屏展示 ↘ Spark 离线分析 → 用户画像 -
优势:Kafka 高吞吐(百万级/秒),持久化存储,支持重放。
1.三大开源消息队列的特征和差异
| 特性 | Apache Kafka | RabbitMQ | Apache RocketMQ |
|---|---|---|---|
| 设计目标 | 高吞吐、日志流、事件溯源 | 可靠传递、复杂路由 | 高可靠、金融级事务、低延迟 |
| 吞吐量 | ⭐⭐⭐⭐⭐(10万~百万+/秒) | ⭐⭐(万级/秒) | ⭐⭐⭐⭐(10万+/秒) |
| 延迟 | 中(毫秒级) | 低(微秒~毫秒) | 低(毫秒级) |
| 消息可靠性 | 高(可配置) | 极高(持久化+ACK) | 极高(同步刷盘+主从) |
| 消息模型 | 发布/订阅(基于 Topic + 分区) | AMQP(Exchange + Queue) | 发布/订阅 + 点对点 |
| 事务支持 | ✅(Exactly-Once,需开启) | ❌(仅 confirm 模式) | ✅(分布式事务,2PC) |
| 顺序消息 | ✅(分区级有序) | ❌(不保证全局有序) | ✅(严格全局有序) |
| 运维复杂度 | 中高(需理解分区/副本) | 低(开箱即用) | 中(NameServer + Broker) |
| 典型用户 | LinkedIn、Netflix、阿里日志 | 微服务解耦、任务队列 | 阿里电商、金融交易 |
1.1Apache Kafka ------ "大数据管道之王"
✅ 核心优势:
-
超高吞吐:顺序写磁盘 + 批处理 + 零拷贝,轻松支撑百万级 TPS;
-
持久化存储:消息可保留数天至数月,支持重放;
-
水平扩展:增加 Broker 节点即可扩容;
-
生态系统强大:Kafka Streams、ksqlDB、Connect 等。
⚠️ 局限:
-
延迟较高(不适合实时交互);
-
不支持复杂路由(如通配符、优先级队列);
-
运维需理解 ISR、Controller、分区再平衡等概念。
🎯 适用场景:
-
日志收集(ELK 替代方案)
-
用户行为分析(埋点数据流)
-
事件溯源 / CQRS
-
大数据 pipeline(Flink / Spark Streaming 数据源)
-
异步解耦(高吞吐场景)
💡 典型例子 :
订单消息 → Kafka → 实时计算引擎 → 大屏监控
1.2 RabbitMQ ------ "企业级可靠消息中间件"
✅ 核心优势:
-
协议标准:基于 AMQP 0.9.1,多语言支持好;
-
灵活路由:支持 Direct、Topic、Fanout、Headers 四种 Exchange;
-
消息确认机制:Publisher Confirm + Consumer ACK,确保不丢;
-
管理界面友好:Web UI 监控队列、连接、速率;
-
插件生态:延迟队列、MQTT、STOMP 等。
⚠️ 局限:
-
吞吐量较低(单队列约 1~2 万/秒);
-
集群模式(Mirrored Queue)扩展性差;
-
内存占用高(消息先入内存)。
🎯 适用场景:
- 微服务间可靠通信(订单 → 库存 → 物流)
- 任务队列(邮件发送、图片处理)
- 需要复杂路由的场景 (如:
order.*.paid匹配多个服务) - 对消息可靠性要求极高,但吞吐不高
💡 典型例子 :
用户注册 → 发送欢迎邮件(RabbitMQ 延迟队列 5 分钟后触发)
1.3Apache RocketMQ ------ "金融级消息引擎"
✅ 核心优势:
-
高可靠 + 低延迟:同步双写 + 主从架构,金融级 SLA;
-
严格顺序消息:同一订单 ID 的消息全局有序;
-
事务消息:两阶段提交,解决"本地事务 + 发消息"一致性;
-
消息轨迹:可追踪消息全生命周期;
-
削峰填谷:支持海量堆积(TB 级)。
⚠️ 局限:
-
社区活跃度略低于 Kafka;
-
生态工具较少(无原生流处理);
-
配置稍复杂(NameServer + Broker + Producer/Consumer)。
🎯 适用场景:
-
电商交易系统(下单、支付、履约)
-
金融核心链路(转账、清算)
-
需要分布式事务的场景
-
强顺序要求(如:股票交易指令)
用户支付成功 → RocketMQ 事务消息 → 更新订单状态 + 扣减库存(原子性)
2.使用
2.1RabbitMQ使用
1.安装
我们可以在自己的云服务器安装RabbitMQ服务(网上教程很多,此步省略),还可以通过云服务直接购买,生产环境我们推荐云服务商的产品,稳定可靠
2.导入依赖(我们以java语言为例子)
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3.编码
yml配置文件如下:
java
spring
rabbitmq:
host: 换成你的
port: 5672
username: xxx
password: xxx
简单解释一下下面这代代码的意思:创建私信交换器,创建两个队列,生成死信,发送延迟消息的方法,发送端调用sendDelayedMessage方法即可
java
/**
* @Author xjy
* @Date 2025/4/17 14:37
*/
@Configuration
public class RabbitMQConfig {
@Resource
private RabbitTemplate template;
//1.创建 死信交换器 专门转发死信消息
@Bean("dead")
public DirectExchange createDLX(){
return new DirectExchange("deadex");
}
//2.创建队列
//队列 生成死信 1.没有消费者 2.有效期 3.设置死信和死信匹配关键字
@Bean
public Queue createQ1() {
Map<String, Object> param = new HashMap<>();
//设置死信交换器
param.put("x-dead-letter-exchange", "deadex");
//设置死信交换器 匹配的路由
param.put("x-dead-letter-routing-key", "demo");
return QueueBuilder.durable("quene01").withArguments(param).build();
}
@Bean
public Queue createQ2(){
return new Queue("quene02");
}
//3.绑定
@Bean
public Binding createBd8(@Qualifier("dead") DirectExchange dead){
//any 任意1个即可
return BindingBuilder.bind(createQ2()).to(dead).with("demo");
}
/**
* 发送带有动态 TTL 的消息
*
* @param messageContent 消息内容
* @param ttlMillis 消息的 TTL(单位:毫秒)
*/
public void sendDelayedMessage(String messageContent, long ttlMillis) {
// 创建消息属性对象
MessageProperties messageProperties = new MessageProperties();
// 设置消息的 TTL
messageProperties.setExpiration(String.valueOf(ttlMillis));
// 设置消息的内容类型为 text/plain
messageProperties.setContentType("text/plain");
// 设置字符集为 UTF-8
messageProperties.setContentEncoding("UTF-8");
// 创建消息对象
Message message = new Message(messageContent.getBytes(StandardCharsets.UTF_8), messageProperties);
// 发送消息
template.send("quene01", message);
}
}
消费端处理延迟消息如下:
java
@Component
@RabbitListener(queues = "quene02")
public class DqListener {
@RabbitHandler
public void handler(String msg){
//延迟接收消息,处理业务即可
}
}
}
到此,我们基于死信消息实现的延迟队列就实现了,基于这个逻辑同样可以实现订单超时取消,用户注册 → 发送欢迎邮件(RabbitMQ 延迟队列 5 分钟后触发)等功能
测试功能如下:
2.2RocketMQ使用
安装省略,同样的,你可以在服务器上自定义安装,或者使用钞能力购买云服务产品
导入依赖
java
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client-java</artifactId>
<version>5.3.0</version>
</dependency>
生产者:
java
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.SendReceipt;
import org.apache.rocketmq.client.java.ClientConfig;
import org.apache.rocketmq.client.java.producer.ProducerBuilder;
import java.util.Collections;
public class OrderProducer {
// 替换为您的 NameServer 地址(多个用分号隔开)
private static final String ENDPOINTS = "192.168.1.10:9876;192.168.1.11:9876";
public static void main(String[] args) throws ClientException {
// 1. 创建 Producer 配置
ClientConfig config = ClientConfig.newBuilder()
.setEndpoints(ENDPOINTS)
.build();
// 2. 构建 Producer
Producer producer = ProducerBuilder.newBuilder(config)
.setTopics(Collections.singletonList("ORDER_TOPIC")) // 预声明 Topic(可选)
.build();
// 3. 发送消息
String messageBody = "{\"orderId\":\"1001\",\"action\":\"CREATE\"}";
SendReceipt sendReceipt = producer.send(
"ORDER_TOPIC", // Topic
"ORDER_GROUP", // Tag(可选,用于过滤)
messageBody.getBytes() // 消息体(byte[])
);
System.out.println("Message sent! Message ID: " + sendReceipt.getMessageId());
producer.close();
}
}
消费者:
java
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.consumer.ConsumeResult;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.PushConsumer;
import org.apache.rocketmq.client.java.ClientConfig;
import org.apache.rocketmq.client.java.consumer.ConsumeResultStatus;
import org.apache.rocketmq.client.java.consumer.PushConsumerBuilder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
public class OrderConsumer {
private static final String ENDPOINTS = "192.168.1.10:9876;192.168.1.11:9876";
public static void main(String[] args) throws ClientException, InterruptedException {
// 1. 创建 Consumer 配置
ClientConfig config = ClientConfig.newBuilder()
.setEndpoints(ENDPOINTS)
.build();
// 2. 构建 Consumer
PushConsumer consumer = PushConsumerBuilder.newBuilder(config)
.setSubscriptionExpressions(Collections.singletonMap(
"ORDER_TOPIC",
FilterExpression.SUB_ALL // 订阅所有消息(或指定 Tag)
))
.setMessageListener(messageView -> {
try {
// 解析消息
String body = new String(messageView.getBody(), StandardCharsets.UTF_8);
String topic = messageView.getTopic();
String tag = messageView.getTag(); // 可能为 null
System.out.println("Received message: " + body);
// TODO: 处理业务逻辑(如更新订单状态)
return ConsumeResult.SUCCESS; // 成功
} catch (Exception e) {
e.printStackTrace();
return ConsumeResult.FAILURE; // 失败,会重试
}
})
.build();
System.out.println("Consumer started...");
// 保持进程运行
Thread.sleep(Long.MAX_VALUE);
}
}
2.1Kafka使用
安装省略,同样的,你可以在服务器上自定义安装,或者使用钞能力购买云服务产品
导入依赖
java
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
yml配置文件
java
spring:
kafka:
bootstrap-servers: # Kafka 集群地址,确保多个服务器,提高可靠性
consumer:
group-id: "" # 消费者组 ID
auto-offset-reset: latest # 自动偏移量重置为最新,避免每次启动都从头消费
enable-auto-commit: true # 自动提交偏移量
auto-commit-interval: 1000 # 自动提交偏移量的间隔时间(毫秒)
max-poll-records: 500 # 每次批量拉取消息的最大数量
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的键序列化器
value-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的值序列化器
生产者:
java
@Resource
private KafkaTemplate<String, String> kafkaTemplate;
@Resource
private ObjectMapper objectMapper;
//每一分钟执行一次
@Scheduled(cron = "0 0/1 * * * ?")
public void handlerOrder() {
//模拟发送消息 vopBizTransMessage为任意对象,可以改成自己的类对象即可,在消费端解析,key为统一标识,可以传订单id,商品id这些,可以分组有序
kafkaTemplate.send("换成你的topic主题", "key", objectMapper.writeValueAsString(vopBizTransMessage));
}
消费者:
java
@Slf4j
@Component
public class JdVopOrderConsumer {
private final ObjectMapper objectMapper = new ObjectMapper();
@KafkaListener(
topics = "jd_vop_order",
groupId = "jd_vop_message_dev" // 必须与配置中的 group-id 一致
)
public void listen(String message) {
try {
log.info("Received raw message: {}", message);
// 反序列化为业务对象
VopBizTransMessage vopMessage = objectMapper.readValue(message, VopBizTransMessage.class);
// TODO: 处理业务逻辑(如更新订单状态、触发后续流程等)
processOrder(vopMessage);
} catch (Exception e) {
log.error("Failed to process Kafka message: {}", message, e);
// TODO: 考虑死信队列或重试机制(见下文)
}
}
private void processOrder(VopBizTransMessage message) {
// 示例:打印订单号
log.info("Processing order: {}", message.getContent());
// 实际业务:调用订单服务、库存服务等
}
}
到此,我们就实现了消息队列的生产和消费,下面我们开始测试流程: