沉默是金,总会发光
大家好,我是沉默
很多人用 RabbitMQ,只知道拿来“发消息”,其实它背后的高级特性才是真正撑起高可用、高并发系统的筋骨。
本文将带你基于 Spring Boot 实战 6 大核心特性,从削峰填谷的限流,到按优先级发货的队列处理,干货满满,一套搞定!
**-**01-
消费端限流
限流就是削峰填谷的“节流阀”
假设你的系统 QPS 峰值只能撑 1000,请求量一口气飙到 5000,直接炸。
加入 MQ,消息先入队再慢慢处理,设置消费端限速为 1000/QPS,虽然消息积压了,但系统活着。
目的:保护消费者,不让它被突发流量压垮。
实现方式:配置 + 手动签收 + 限速
开启限流的前提:必须手动签收,否则自动签收=全部接收=根本没限流。
YML 配置:
spring:
rabbitmq:
host: 192.168.0.162
port: 5672
username: zzzzzt
password: zzzzzt
virtual-host: /
listener:
simple:
acknowledge-mode: manual # 手动签收
prefetch: 5 # 每次最多拉5条消息
消费者代码:
@Component
public class QosConsumer {
@RabbitListener(queues = "my_queue")
public void listenMessage(Message message, Channel channel) throws IOException, InterruptedException {
System.out.println(new String(message.getBody()));
Thread.sleep(3000); // 模拟业务处理
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true); // 手动签收
}
}
**-**02-
不公平分发
多个消费者监听同一个队列,RabbitMQ 默认轮询分发。
但现实中,总有“优等生”和“慢吞吞”。
这时候就需要“不公平分发”机制,让谁处理快谁先吃!
实现方式:改 prefetch = 1!
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual
prefetch: 1 # 每次拉1条消息,谁快谁拉
**-**03-
消息存活时间(TTL)
“延迟处理”、“定时关闭未支付订单”场景常用 TTL。
RabbitMQ 支持两种 TTL 方式:
-
整队设置(全员定时清理)
-
单条设置(只炸这一条)
示例代码:
整队 TTL:
@Bean
public Queue getMessageQueue2() {
return QueueBuilder
.durable("bootQueue2")
.ttl(10000) // 每条消息存活10秒
.build();
}
单条 TTL:
@Test
public void testSendMessage() {
MessageProperties props = new MessageProperties();
props.setExpiration("10000"); // 设置10秒TTL
Message message = new Message("Hello TTL".getBytes(), props);
rabbitTemplate.convertAndSend("my_topic_exchange", "my_routing", message);
}
**-**04-
优先级队列
大客户(比如 Apple、京东)订单必须先处理,小客户稍等。
这时候就需要 RabbitMQ 的 优先级队列(Priority Queue) 上场。
实现方式:
定义优先级队列:
@Bean
public Queue priorityQueue() {
return QueueBuilder
.durable("priority_queue")
.maxPriority(10) // 建议不超过10
.build();
}
发送优先级消息:
@Test
public void testPriority() {
for (int i = 0; i < 10; i++) {
if (i == 5) {
MessageProperties props = new MessageProperties();
props.setPriority(9);
Message msg = new Message(("VIP订单" + i).getBytes(), props);
rabbitTemplate.convertAndSend("priority_exchange", "my_routing", msg);
} else {
rabbitTemplate.convertAndSend("priority_exchange", "my_routing", "普通订单" + i);
}
}
}
**-**05-
死信队列(DLX)
什么是死信?
消息“死”了 = 被拒收 or 到期未处理 or 队列爆满。
这些消息不能直接丢,应该转送到死信交换机(DLX),转发到死信队列 Dead Queue里去,做二次处理。
实战代码:
普通队列绑定死信交换机:
@Bean
public Queue normalQueue() {
return QueueBuilder.durable("normal_queue")
.deadLetterExchange("dead_exchange")
.deadLetterRoutingKey("dead_routing")
.ttl(10000)
.maxLength(10)
.build();
}
模拟死信:
@Test
public void testDlx() {
// 消息过期变死信
rabbitTemplate.convertAndSend("normal_exchange", "my_routing", "过期死信");
// 队列满了变死信
for (int i = 0; i < 15; i++) {
rabbitTemplate.convertAndSend("normal_exchange", "my_routing", "满了死信" + i);
}
// 拒签死信
rabbitTemplate.convertAndSend("normal_exchange", "my_routing", "拒签死信");
}
消费者拒签示例:
@Component
public class DlxConsumer {
@RabbitListener(queues = "normal_queue")
public void listenMessage(Message message, Channel channel) throws IOException {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), true, false); // 拒签且不重回队列
}
}
**-**06-
延迟队列
“订单30分钟未支付自动取消”,RabbitMQ 本身没延迟队列,但我们可以用 TTL + 死信机制组合变相实现。
实现方式:
@Bean
public Queue orderDelayQueue() {
return QueueBuilder.durable("order_delay_queue")
.ttl(1800000) // 30分钟
.deadLetterExchange("order_dlx_exchange")
.deadLetterRoutingKey("order_cancel_routing")
.build();
}
然后消费 order_cancel_queue 即可实现“延迟取消订单”。
总结:RabbitMQ 高级特性速览表
| 特性 | 应用场景 | 核心机制 |
|---|
| 消费端限流 | 削峰填谷 | 手动签收 + prefetch 限制 |
|---|
| 不公平分发 | 谁快谁多 | prefetch = 1 |
|---|
| 消息存活时间 TTL | 定时处理/过期丢弃 | 队列 TTL/消息 TTL |
|---|
| 优先级队列 | 大客户优先 | maxPriority + message.setPriority |
|---|
| 死信队列 | 异常消息“转院” | DLX + Routing Key |
|---|
| 延迟队列 | 定时取消/延迟处理任务 | TTL + 死信组合 |
|---|
说说你最想用哪一个特性?遇到过哪些场景?
热门文章
**-**07-
粉丝福利
我这里创建一个程序员成长&副业交流群,和一群志同道合的小伙伴,一起聚焦自身发展,可以聊:技术成长与职业规划,分享路线图、面试经验和效率工具,探讨多种副业变现路径,从写作课程到私活接单,主题活动、打卡挑战和项目组队,让志同道合的伙伴互帮互助、共同进步。如果你对这个特别的群,感兴趣的,可以加一下,微信通过后会拉你入群,但是任何人在群里打任何广告,都会被我T掉。