📖 开场:三峡大坝的智慧
想象长江上的三峡大坝 🏞️:
没有大坝(直接处理):
markdown
暴雨来临 🌧️🌧️🌧️
↓
洪水 🌊🌊🌊
↓
淹没下游城市 😱
有大坝(削峰填谷):
markdown
暴雨来临 🌧️🌧️🌧️
↓
大坝蓄水 🏞️(消息队列)
↓
均匀放水 💧💧💧
↓
下游安全 ✅
这就是消息队列的削峰填谷作用!
核心思想:
- 削峰:高峰时暂存请求
- 填谷:低谷时慢慢处理
🤔 什么是削峰填谷?
削峰(Peak Shaving)
问题:流量突增
makefile
秒杀活动开始!🎉
10:00:00 → 100万请求同时到来 💥
↓
服务器扛不住 → 宕机 💀
解决:消息队列缓冲
makefile
10:00:00 → 100万请求 → 消息队列(暂存)📦
↓
慢慢消费 → 1000 QPS → 服务器稳定 ✅
效果:
- 请求峰值:100万/秒
- 处理能力:1000/秒
- 消息队列削掉了99.9%的峰值!
填谷(Valley Filling)
问题:流量低谷时资源浪费
markdown
凌晨3点 🌙
↓
请求量很少 → 服务器闲置 💤
解决:积压的消息继续处理
markdown
白天积压的消息
↓
晚上慢慢处理 → 填满低谷时间 ⏰
↓
资源充分利用 ✅
🎯 削峰填谷的原理
原理图
markdown
请求流量曲线图
流量
↑
│ 💥 峰值(100万QPS)
│ / \
│ / \
│ / \ ← 没有消息队列:直接承受峰值
│ / \ 系统崩溃!💀
│ / \
│─────────────────────── 系统处理能力(1000 QPS)
│ \
│ __ 低谷(100 QPS)
│
└─────────────────────────────────→ 时间
10:00 12:00
有消息队列的情况
流量
↑
│ 消息队列暂存 📦
│ / \
│ / \
│ / \
│ / \
│ / \
│─────────────────────── 稳定处理(1000 QPS)✅
│ \
│ __ 继续处理积压 ⏰
│
└─────────────────────────────────→ 时间
10:00 12:00
削峰:暂存峰值请求
填谷:利用低谷时间处理积压
数学模型
ini
假设:
- 峰值流量:P = 100万 QPS
- 系统处理能力:C = 1000 QPS
- 峰值持续时间:T = 60秒
没有消息队列:
需要的服务器数 = P / C = 100万 / 1000 = 1000台 😱
有消息队列:
积压的消息数 = (P - C) × T
= (100万 - 1000) × 60
≈ 6000万条
处理完需要的时间 = 6000万 / 1000 = 60000秒 ≈ 16.7小时
需要的服务器数 = 只需1台(按处理能力配置)✅
结论:消息队列可以节省999台服务器!💰
🚀 实际应用场景
场景1:秒杀系统 🛒
业务流程
arduino
用户点击"秒杀" 🖱️
↓
请求进入消息队列 📦(瞬间完成)
↓
返回"排队中"提示 ⏳
↓
后台慢慢处理订单 🔄
↓
处理完成,通知用户 ✅
代码实现
秒杀接口(生产者):
java
@RestController
@RequestMapping("/api/seckill")
@Slf4j
public class SeckillController {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String SECKILL_QUEUE = "seckill.queue";
/**
* 秒杀接口
*/
@PostMapping("/order")
public ResponseEntity<?> seckill(
@RequestParam Long productId,
@RequestParam Long userId) {
log.info("用户{}秒杀商品{}", userId, productId);
// ⭐ 检查库存(Redis)
String stockKey = "product:stock:" + productId;
Long stock = redisTemplate.opsForValue().decrement(stockKey);
if (stock < 0) {
// 库存不足,回滚
redisTemplate.opsForValue().increment(stockKey);
return ResponseEntity.ok(Result.fail("商品已抢光!"));
}
// ⭐ 发送到消息队列(削峰)
SeckillMessage message = new SeckillMessage();
message.setProductId(productId);
message.setUserId(userId);
message.setTimestamp(System.currentTimeMillis());
rabbitTemplate.convertAndSend(SECKILL_QUEUE, message);
log.info("秒杀请求已进入队列");
// ⭐ 立即返回(不等待处理完成)
return ResponseEntity.ok(Result.success("排队中,请稍后查看订单"));
}
/**
* 查询秒杀结果
*/
@GetMapping("/result")
public ResponseEntity<?> getSeckillResult(
@RequestParam Long userId,
@RequestParam Long productId) {
String resultKey = String.format("seckill:result:%d:%d", userId, productId);
String result = redisTemplate.opsForValue().get(resultKey);
if (result == null) {
return ResponseEntity.ok(Result.success("排队中..."));
} else if ("SUCCESS".equals(result)) {
return ResponseEntity.ok(Result.success("秒杀成功!"));
} else {
return ResponseEntity.ok(Result.fail("秒杀失败:" + result));
}
}
}
@Data
class SeckillMessage {
private Long productId;
private Long userId;
private Long timestamp;
}
秒杀消费者(处理订单):
java
@Component
@Slf4j
public class SeckillConsumer {
@Autowired
private OrderService orderService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* ⭐ 消费秒杀消息(填谷)
*
* 配置:
* - 并发数:10(根据系统处理能力配置)
* - prefetchCount:1(每次只取1条消息)
*/
@RabbitListener(
queues = "seckill.queue",
concurrency = "10" // 10个消费者线程
)
public void processSeckill(SeckillMessage message) {
log.info("处理秒杀订单: userId={}, productId={}",
message.getUserId(), message.getProductId());
String resultKey = String.format("seckill:result:%d:%d",
message.getUserId(), message.getProductId());
try {
// ⭐ 创建订单(数据库操作)
Order order = orderService.createSeckillOrder(
message.getUserId(),
message.getProductId()
);
log.info("秒杀订单创建成功: orderId={}", order.getId());
// 保存结果到Redis
redisTemplate.opsForValue().set(resultKey, "SUCCESS", 10, TimeUnit.MINUTES);
// 发送通知
notifyUser(message.getUserId(), "秒杀成功!订单号:" + order.getId());
} catch (InsufficientStockException e) {
log.warn("库存不足");
redisTemplate.opsForValue().set(resultKey, "库存不足", 10, TimeUnit.MINUTES);
} catch (Exception e) {
log.error("秒杀订单处理失败", e);
redisTemplate.opsForValue().set(resultKey, "系统繁忙,请稍后再试", 10, TimeUnit.MINUTES);
}
}
private void notifyUser(Long userId, String message) {
// 发送短信、推送通知等
log.info("通知用户{}: {}", userId, message);
}
}
配置消息队列:
java
@Configuration
public class SeckillQueueConfig {
/**
* ⭐ 秒杀队列
*
* 配置:
* - 持久化:防止消息丢失
* - 不自动删除:队列一直存在
*/
@Bean
public Queue seckillQueue() {
return QueueBuilder.durable("seckill.queue")
// ⭐ 设置队列最大长度(防止内存溢出)
.maxLength(1000000) // 最多100万条消息
// ⭐ 超出长度的消息策略:拒绝
.overflow(QueueBuilder.Overflow.rejectPublish)
.build();
}
}
压测对比
没有消息队列:
diff
压测工具:JMeter
并发用户:10万
请求时间:10秒
结果:
- 平均响应时间:8秒
- 成功率:30%
- 服务器CPU:100%
- 数据库连接:全部耗尽
- 结论:系统崩溃 💀
有消息队列:
diff
压测工具:JMeter
并发用户:10万
请求时间:10秒
结果(秒杀接口):
- 平均响应时间:50ms ✅
- 成功率:100% ✅
- 服务器CPU:20% ✅
结果(订单处理):
- 处理速度:1000 QPS(稳定)
- 全部处理完成时间:100秒
- 结论:系统稳定运行 ✅
场景2:订单系统 📦
业务场景
markdown
电商大促(双11)🎉
↓
订单量暴增:平时100倍
↓
订单处理链路长:
- 扣减库存
- 创建订单
- 支付流程
- 发送短信
- 更新积分
- 推送通知
架构设计
css
用户下单
↓
Order API
↓
┌────────────────────────────────────┐
│ 消息队列(削峰) │
│ │
│ ┌─────────────────────────────┐ │
│ │ 创建订单队列 │ │
│ │ (order.create.queue) │ │
│ └─────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────┐ │
│ │ 支付队列 │ │
│ │ (order.payment.queue) │ │
│ └─────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────┐ │
│ │ 通知队列 │ │
│ │ (order.notify.queue) │ │
│ └─────────────────────────────┘ │
└────────────────────────────────────┘
↓
多个消费者并发处理(填谷)
代码实现
下单接口:
java
@RestController
@RequestMapping("/api/order")
@Slf4j
public class OrderController {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 创建订单
*/
@PostMapping("/create")
public ResponseEntity<?> createOrder(@RequestBody OrderRequest request) {
log.info("收到下单请求: {}", request);
// ⭐ 简单校验
validateOrder(request);
// ⭐ 生成订单号
String orderNo = generateOrderNo();
// ⭐ 发送到消息队列
OrderMessage message = new OrderMessage();
message.setOrderNo(orderNo);
message.setUserId(request.getUserId());
message.setProductId(request.getProductId());
message.setQuantity(request.getQuantity());
message.setAmount(request.getAmount());
rabbitTemplate.convertAndSend("order.create.exchange", "order.create", message);
log.info("订单消息已发送: orderNo={}", orderNo);
// ⭐ 立即返回订单号
return ResponseEntity.ok(Result.success(orderNo));
}
private void validateOrder(OrderRequest request) {
if (request.getUserId() == null) {
throw new IllegalArgumentException("用户ID不能为空");
}
// ... 其他校验
}
private String generateOrderNo() {
// 雪花算法生成订单号
return String.valueOf(System.currentTimeMillis());
}
}
订单消费者(链式处理):
java
@Component
@Slf4j
public class OrderConsumer {
@Autowired
private OrderService orderService;
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* ⭐ 第1步:创建订单
*/
@RabbitListener(
queues = "order.create.queue",
concurrency = "20-50" // 动态调整消费者数量
)
public void createOrder(OrderMessage message) {
log.info("创建订单: orderNo={}", message.getOrderNo());
try {
// 创建订单(数据库操作)
Order order = orderService.create(message);
log.info("订单创建成功: orderId={}", order.getId());
// ⭐ 发送到下一个队列(支付)
PaymentMessage paymentMsg = new PaymentMessage();
paymentMsg.setOrderId(order.getId());
paymentMsg.setAmount(order.getAmount());
rabbitTemplate.convertAndSend("order.payment.exchange", "order.payment", paymentMsg);
} catch (Exception e) {
log.error("订单创建失败", e);
// 进入死信队列
throw new RuntimeException(e);
}
}
/**
* ⭐ 第2步:支付处理
*/
@RabbitListener(
queues = "order.payment.queue",
concurrency = "10-30"
)
public void processPayment(PaymentMessage message) {
log.info("处理支付: orderId={}", message.getOrderId());
try {
// 调用支付接口
boolean success = paymentService.pay(message.getOrderId(), message.getAmount());
if (success) {
log.info("支付成功: orderId={}", message.getOrderId());
// ⭐ 发送到下一个队列(通知)
NotifyMessage notifyMsg = new NotifyMessage();
notifyMsg.setOrderId(message.getOrderId());
notifyMsg.setType("PAYMENT_SUCCESS");
rabbitTemplate.convertAndSend("order.notify.exchange", "order.notify", notifyMsg);
}
} catch (Exception e) {
log.error("支付处理失败", e);
throw new RuntimeException(e);
}
}
/**
* ⭐ 第3步:发送通知
*/
@RabbitListener(
queues = "order.notify.queue",
concurrency = "5-10"
)
public void sendNotification(NotifyMessage message) {
log.info("发送通知: orderId={}", message.getOrderId());
try {
// 发送短信
smsService.send(message);
// 推送通知
pushService.send(message);
log.info("通知发送成功: orderId={}", message.getOrderId());
} catch (Exception e) {
log.error("通知发送失败", e);
// 通知失败不影响订单,不抛异常
}
}
}
场景3:日志收集系统 📊
业务场景
markdown
大型系统:1000台服务器
每台服务器:1000 QPS
每个请求:产生10条日志
↓
总日志量:1000万条/秒 🔥
↓
直接写入数据库?系统崩溃!💀
架构设计
yaml
┌──────────────────────────────────────────┐
│ 应用服务器(1000台) │
│ │
│ App1 → 日志 → 消息队列 📦 │
│ App2 → 日志 → 消息队列 📦 │
│ App3 → 日志 → 消息队列 📦 │
│ ... │
└─────────────┬────────────────────────────┘
│
↓ 削峰(异步发送)
┌──────────────────────────────────────────┐
│ Kafka(消息队列) │
│ │
│ Topic: application-logs │
│ Partitions: 100 │
│ Retention: 7 days │
└─────────────┬────────────────────────────┘
│
↓ 填谷(批量消费)
┌──────────────────────────────────────────┐
│ 日志处理服务(10台) │
│ │
│ Consumer1 → 批量写入 → Elasticsearch │
│ Consumer2 → 批量写入 → Elasticsearch │
│ ... │
└──────────────────────────────────────────┘
代码实现
日志生产者:
java
@Component
@Slf4j
public class LogProducer {
@Autowired
private KafkaTemplate<String, LogMessage> kafkaTemplate;
private static final String TOPIC = "application-logs";
/**
* ⭐ 异步发送日志(削峰)
*/
public void sendLog(LogMessage logMessage) {
// 异步发送,不等待响应
kafkaTemplate.send(TOPIC, logMessage.getAppName(), logMessage)
.addCallback(
success -> {
// 发送成功(可选记录)
},
failure -> {
// 发送失败(记录本地日志)
log.error("日志发送失败", failure);
}
);
}
}
日志消费者(批量处理):
java
@Component
@Slf4j
public class LogConsumer {
@Autowired
private ElasticsearchTemplate esTemplate;
/**
* ⭐ 批量消费日志(填谷)
*/
@KafkaListener(
topics = "application-logs",
groupId = "log-consumer-group",
concurrency = "10" // 10个消费者线程
)
public void consumeLogs(List<ConsumerRecord<String, LogMessage>> records) {
log.info("收到{}条日志", records.size());
// ⭐ 批量处理(提高效率)
List<LogMessage> logs = records.stream()
.map(ConsumerRecord::value)
.collect(Collectors.toList());
try {
// ⭐ 批量写入Elasticsearch
esTemplate.bulkIndex(logs);
log.info("批量写入{}条日志成功", logs.size());
} catch (Exception e) {
log.error("批量写入失败", e);
// 重试或写入死信队列
}
}
}
Kafka消费者配置:
yaml
spring:
kafka:
consumer:
# ⭐ 批量消费配置(填谷)
max-poll-records: 1000 # 每次拉取1000条
fetch-min-size: 10240 # 最小拉取10KB
fetch-max-wait: 500 # 最多等待500ms
# 批量监听器
listener:
type: batch # 批量模式
📊 效果对比
性能对比
| 指标 | 没有消息队列 | 有消息队列 | 提升 |
|---|---|---|---|
| 峰值QPS | 1000(系统极限) | 100万(队列缓冲) | 1000倍 ✅ |
| 平均响应时间 | 8秒 | 50ms | 160倍 ✅ |
| 成功率 | 30% | 100% | 3.3倍 ✅ |
| 服务器数量 | 1000台 | 10台 | 节省99% ✅ |
| 系统稳定性 | 崩溃 💀 | 稳定运行 ✅ | 无价 🎉 |
成本对比
markdown
假设场景:电商大促
没有消息队列:
- 服务器:1000台 × 5000元/月 = 500万/月
- 数据库:100个实例 × 1万元/月 = 100万/月
- 总成本:600万/月 💰💰💰
有消息队列:
- 服务器:10台 × 5000元/月 = 5万/月
- 数据库:10个实例 × 1万元/月 = 10万/月
- 消息队列:5万/月
- 总成本:20万/月 💰
节省成本:580万/月(97%) 🎉
🎯 削峰填谷的最佳实践
1️⃣ 合理设置队列长度 📏
java
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
// ⭐ 设置最大长度(防止内存溢出)
.maxLength(1000000) // 根据实际情况调整
// ⭐ 超出长度的策略
.overflow(QueueBuilder.Overflow.rejectPublish) // 拒绝新消息
.build();
}
计算公式:
diff
队列长度 = 峰值QPS × 峰值持续时间 × 安全系数
例如:
- 峰值QPS:10万
- 峰值持续时间:60秒
- 安全系数:2
- 队列长度 = 10万 × 60 × 2 = 1200万
2️⃣ 动态调整消费者数量 🔄
java
@RabbitListener(
queues = "order.queue",
concurrency = "10-50" // ⭐ 最小10个,最大50个消费者
)
public void consumeOrder(OrderMessage message) {
// 处理订单
}
调整策略:
yaml
队列长度 < 1000:10个消费者(低谷)
队列长度 > 10000:50个消费者(高峰)
3️⃣ 监控告警 📊
java
@Component
@Slf4j
public class QueueMonitor {
@Autowired
private RabbitAdmin rabbitAdmin;
/**
* 每分钟检查队列长度
*/
@Scheduled(fixedRate = 60000)
public void monitorQueue() {
Properties props = rabbitAdmin.getQueueProperties("order.queue");
if (props != null) {
Integer messageCount = (Integer) props.get("QUEUE_MESSAGE_COUNT");
log.info("队列长度: {}", messageCount);
// ⭐ 告警阈值
if (messageCount > 100000) {
sendAlert("队列积压严重:" + messageCount + "条消息");
}
}
}
private void sendAlert(String message) {
// 发送告警(短信、邮件、钉钉等)
log.error("🚨 告警: {}", message);
}
}
4️⃣ 设置消息TTL ⏰
java
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
// ⭐ 消息TTL(30分钟)
.ttl(1800000) // 超时的消息自动删除
// ⭐ 配置死信队列
.deadLetterExchange("dlx.exchange")
.build();
}
作用:
- 防止消息积压过多
- 及时清理过期消息
- 保证时效性
5️⃣ 流控策略 🚦
java
@Configuration
public class RateLimiterConfig {
@Bean
public RateLimiter rateLimiter() {
// ⭐ 限流器:每秒最多1000个请求
return RateLimiter.create(1000);
}
}
@RestController
public class OrderController {
@Autowired
private RateLimiter rateLimiter;
@PostMapping("/order")
public ResponseEntity<?> createOrder(@RequestBody OrderRequest request) {
// ⭐ 尝试获取令牌(等待最多1秒)
boolean acquired = rateLimiter.tryAcquire(1, TimeUnit.SECONDS);
if (!acquired) {
return ResponseEntity.status(429)
.body(Result.fail("系统繁忙,请稍后再试"));
}
// 正常处理...
return ResponseEntity.ok(Result.success("订单已提交"));
}
}
🎓 面试题速答
Q1: 什么是削峰填谷?
A : 削峰填谷 = 消息队列的核心作用之一
削峰:
- 高峰时暂存请求到消息队列
- 避免瞬时流量压垮系统
填谷:
- 低谷时继续处理积压的消息
- 充分利用系统资源
比喻:像三峡大坝,洪水来临时蓄水,平时慢慢放水
Q2: 削峰填谷的典型应用场景?
A: 三大场景:
-
秒杀系统 🛒
- 10万用户同时秒杀
- 消息队列缓冲,后台慢慢处理
-
订单系统 📦
- 大促期间订单量暴增
- 分阶段处理:创建订单 → 支付 → 通知
-
日志收集 📊
- 1000万条日志/秒
- 批量消费,批量写入数据库
Q3: 如何设计一个秒杀系统?
A : 核心:消息队列削峰
java
// 秒杀接口:立即返回
@PostMapping("/seckill")
public Result seckill(Long productId, Long userId) {
// 1. 检查库存(Redis)
Long stock = redisTemplate.decrement("stock:" + productId);
if (stock < 0) {
return Result.fail("已抢光");
}
// 2. 发送到消息队列
rabbitTemplate.send("seckill.queue", message);
// 3. 立即返回
return Result.success("排队中");
}
// 消费者:慢慢处理
@RabbitListener(queues = "seckill.queue", concurrency = "10")
public void processSeckill(SeckillMessage msg) {
// 创建订单(数据库操作)
orderService.create(msg);
}
关键点:
- 库存预扣(Redis)
- 异步处理(消息队列)
- 立即返回(用户体验)
Q4: 队列长度如何设置?
A : 计算公式:
队列长度 = 峰值QPS × 峰值持续时间 × 安全系数
示例:
- 峰值QPS:10万
- 峰值持续时间:60秒
- 安全系数:2
- 队列长度 = 1200万
配置:
java
QueueBuilder.durable("order.queue")
.maxLength(12000000) // 1200万
.overflow(Overflow.rejectPublish) // 超出拒绝
.build();
Q5: 如何监控队列积压?
A : 三种方法:
- 定时任务监控:
java
@Scheduled(fixedRate = 60000)
public void monitorQueue() {
Integer count = rabbitAdmin.getQueueProperties("order.queue")
.get("QUEUE_MESSAGE_COUNT");
if (count > 100000) {
sendAlert("队列积压:" + count);
}
}
-
监控平台:
- Prometheus + Grafana
- 可视化监控大屏
-
消息队列自带监控:
- RabbitMQ Management
- Kafka Manager
Q6: 削峰填谷的优缺点?
A : 优点 ✅:
- 提高系统稳定性(不会被流量压垮)
- 节省服务器成本(按平均值配置)
- 提升用户体验(快速响应)
- 充分利用资源(低谷时间也在处理)
缺点 ❌:
- 实时性下降(异步处理有延迟)
- 架构复杂度增加(引入消息队列)
- 需要监控和运维(队列积压)
适用场景:
- 流量波动大的系统 ✅
- 对实时性要求不高的业务 ✅
- 不适用:实时交易、实时通信 ❌
🎬 总结
markdown
削峰填谷完整流程图
┌────────────────────────────────────────────────┐
│ 用户请求(高峰) │
│ │
│ 👤👤👤👤👤👤👤👤👤👤👤👤👤👤👤 │
│ 100万 QPS(瞬时峰值)💥 │
└─────────────┬──────────────────────────────────┘
│
↓ 削峰(异步发送)
┌────────────────────────────────────────────────┐
│ 消息队列(缓冲区)📦 │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ 消息1 消息2 消息3 ... 消息N │ │
│ └──────────────────────────────────────────┘ │
│ │
│ 暂存消息,削掉峰值 ✅ │
└─────────────┬──────────────────────────────────┘
│
↓ 填谷(稳定消费)
┌────────────────────────────────────────────────┐
│ 消费者(按系统能力处理) │
│ │
│ Consumer1 → 1000 QPS │
│ Consumer2 → 1000 QPS │
│ Consumer3 → 1000 QPS │
│ ... │
│ │
│ 稳定处理,填满低谷时间 ✅ │
└────────────────────────────────────────────────┘
消息队列 = 三峡大坝 🏞️
🎉 恭喜你!
你已经完全掌握了消息队列的削峰填谷作用和实际应用!🎊
核心要点:
- 削峰:高峰时暂存请求,避免系统崩溃
- 填谷:低谷时处理积压,充分利用资源
- 场景:秒杀、大促、日志收集等高并发场景
- 效果:提升100倍性能,节省99%成本
下次面试,这样回答:
"消息队列的削峰填谷是指在流量高峰时将请求暂存到队列中,避免瞬时流量压垮系统;在流量低谷时继续处理积压的消息,充分利用系统资源。
典型应用场景是秒杀系统。10万用户同时秒杀,如果直接处理,数据库会被压垮。使用消息队列后,秒杀接口立即返回'排队中',消息进入队列,后台10个消费者慢慢处理,1000 QPS稳定运行。
队列长度的计算公式是:峰值QPS × 峰值持续时间 × 安全系数。还需要配置动态消费者数量、监控告警、消息TTL等。
我们项目的订单系统在大促期间通过消息队列削峰,响应时间从8秒降到50ms,成功率从30%提升到100%,节省了99%的服务器成本。"
面试官:👍 "非常好!你对削峰填谷理解很深刻,而且有实际应用经验!"
本文完 🎬
上一篇 : 192-死信队列的作用和处理策略.md
下一篇 : 194-消息队列如何保证消息有序性.md
作者注 :写完这篇,我都想去水利局工作了!🏞️
如果这篇文章对你有帮助,请给我一个Star⭐!