🌊 消息队列的削峰填谷作用和实际应用:洪水调节大师!

📖 开场:三峡大坝的智慧

想象长江上的三峡大坝 🏞️:

没有大坝(直接处理)

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: 三大场景:

  1. 秒杀系统 🛒

    • 10万用户同时秒杀
    • 消息队列缓冲,后台慢慢处理
  2. 订单系统 📦

    • 大促期间订单量暴增
    • 分阶段处理:创建订单 → 支付 → 通知
  3. 日志收集 📊

    • 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 : 三种方法

  1. 定时任务监控
java 复制代码
@Scheduled(fixedRate = 60000)
public void monitorQueue() {
    Integer count = rabbitAdmin.getQueueProperties("order.queue")
        .get("QUEUE_MESSAGE_COUNT");
    
    if (count > 100000) {
        sendAlert("队列积压:" + count);
    }
}
  1. 监控平台

    • Prometheus + Grafana
    • 可视化监控大屏
  2. 消息队列自带监控

    • RabbitMQ Management
    • Kafka Manager

Q6: 削峰填谷的优缺点?

A : 优点 ✅:

  • 提高系统稳定性(不会被流量压垮)
  • 节省服务器成本(按平均值配置)
  • 提升用户体验(快速响应)
  • 充分利用资源(低谷时间也在处理)

缺点 ❌:

  • 实时性下降(异步处理有延迟)
  • 架构复杂度增加(引入消息队列)
  • 需要监控和运维(队列积压)

适用场景

  • 流量波动大的系统 ✅
  • 对实时性要求不高的业务 ✅
  • 不适用:实时交易、实时通信 ❌

🎬 总结

markdown 复制代码
          削峰填谷完整流程图

┌────────────────────────────────────────────────┐
│            用户请求(高峰)                     │
│                                                │
│  👤👤👤👤👤👤👤👤👤👤👤👤👤👤👤                 │
│  100万 QPS(瞬时峰值)💥                       │
└─────────────┬──────────────────────────────────┘
              │
              ↓ 削峰(异步发送)
┌────────────────────────────────────────────────┐
│           消息队列(缓冲区)📦                  │
│                                                │
│  ┌──────────────────────────────────────────┐ │
│  │ 消息1  消息2  消息3  ... 消息N          │ │
│  └──────────────────────────────────────────┘ │
│                                                │
│  暂存消息,削掉峰值 ✅                         │
└─────────────┬──────────────────────────────────┘
              │
              ↓ 填谷(稳定消费)
┌────────────────────────────────────────────────┐
│        消费者(按系统能力处理)                 │
│                                                │
│  Consumer1 → 1000 QPS                         │
│  Consumer2 → 1000 QPS                         │
│  Consumer3 → 1000 QPS                         │
│  ...                                           │
│                                                │
│  稳定处理,填满低谷时间 ✅                     │
└────────────────────────────────────────────────┘

         消息队列 = 三峡大坝 🏞️

🎉 恭喜你!

你已经完全掌握了消息队列的削峰填谷作用和实际应用!🎊

核心要点

  1. 削峰:高峰时暂存请求,避免系统崩溃
  2. 填谷:低谷时处理积压,充分利用资源
  3. 场景:秒杀、大促、日志收集等高并发场景
  4. 效果:提升100倍性能,节省99%成本

下次面试,这样回答

"消息队列的削峰填谷是指在流量高峰时将请求暂存到队列中,避免瞬时流量压垮系统;在流量低谷时继续处理积压的消息,充分利用系统资源。

典型应用场景是秒杀系统。10万用户同时秒杀,如果直接处理,数据库会被压垮。使用消息队列后,秒杀接口立即返回'排队中',消息进入队列,后台10个消费者慢慢处理,1000 QPS稳定运行。

队列长度的计算公式是:峰值QPS × 峰值持续时间 × 安全系数。还需要配置动态消费者数量、监控告警、消息TTL等。

我们项目的订单系统在大促期间通过消息队列削峰,响应时间从8秒降到50ms,成功率从30%提升到100%,节省了99%的服务器成本。"

面试官:👍 "非常好!你对削峰填谷理解很深刻,而且有实际应用经验!"


本文完 🎬

上一篇 : 192-死信队列的作用和处理策略.md
下一篇 : 194-消息队列如何保证消息有序性.md

作者注 :写完这篇,我都想去水利局工作了!🏞️

如果这篇文章对你有帮助,请给我一个Star⭐!

复制代码
相关推荐
bug攻城狮4 小时前
Spring Boot 2.6+ 整合 PageHelper 启动报错:循环依赖解决方案全解析
java·spring boot·后端
IT_陈寒4 小时前
Vue 3.4性能优化实战:5个鲜为人知的Composition API技巧让打包体积减少40%
前端·人工智能·后端
大厂码农老A5 小时前
我带的外包兄弟放弃大厂转正,薪资翻倍入职字节
java·后端·面试
武子康5 小时前
大数据-136 - ClickHouse 集群 表引擎详解 选型实战:TinyLog/Log/StripeLog/Memory/Merge
大数据·分布式·后端
Somehow0075 小时前
从Binlog到消息队列:构建可靠的本地数据同步管道(macOS本地部署Canal & RocketMQ并打通全流程)
后端·架构
ai安歌5 小时前
【Rust编程:从新手到大师】Rust概述
开发语言·后端·rust
用户6120414922135 小时前
C语言做的智能家居控制模拟系统
c语言·后端·敏捷开发
豆苗学前端5 小时前
10分钟带你入门websocket,并实现一个在线多人聊天室
前端·javascript·后端
风霜不见闲沉月5 小时前
rust更新后编译的exe文件执行报错
开发语言·后端·rust