欢迎光临
我们一直在努力

消息队列为什么能削峰?异步、解耦、削峰到底是什么意思

消息队列的核心作用是:异步、解耦、削峰。

这三个词看起来很简单,但如果只是记住定义,其实很难真正理解消息队列到底解决了什么问题。

本文不从 RabbitMQ、Kafka 或 RocketMQ 的具体 API 开始,而是先从一个普通的业务场景出发,看看为什么系统中需要消息队列。


1. 不使用消息队列会怎么样?

假设现在有一个商城系统,用户下单以后,需要执行下面几个操作:

创建订单

扣减库存

增加积分

发送短信

发送优惠券

如果全部采用同步调用:

def create_order():
save_order()
reduce_stock()
add_points()
send_sms()
send_coupon()

用户点击“提交订单”以后,必须等所有操作执行完成,才能得到响应。

假设每个操作耗时:

创建订单:100ms
扣减库存:100ms
增加积分:200ms
发送短信:500ms
发送优惠券:300ms

那么整个请求大约需要:

100 + 100 + 200 + 500 + 300
= 1200ms

但实际上,用户下单时真正关心的可能只有:

订单有没有创建成功?
库存有没有扣减成功?

至于积分、短信、优惠券,并不一定需要立即完成。

这时候就可以引入消息队列。


2. 异步:不用等所有事情做完

加入 MQ 后,可以把非核心业务发送到消息队列:

用户下单

订单服务
├── 创建订单
├── 扣减库存

└── 发送消息 → MQ

┌───────┼────────┐
↓ ↓ ↓
积分服务 短信服务 优惠券服务

订单服务只需要完成核心操作,然后向 MQ 发送一条消息:

order_created

之后就可以直接告诉用户:

下单成功

积分、短信、优惠券则由其他服务慢慢处理。

原本:

订单 → 库存 → 积分 → 短信 → 优惠券

现在变成:

订单 → 库存 → MQ → 返回结果

这就是消息队列的第一个作用:

异步

所谓异步,可以简单理解成:

这件事情我先通知你,你什么时候处理,不需要我一直等着。

因此 MQ 可以明显降低主流程的响应时间。


3. 解耦:订单服务不需要认识所有系统

继续看刚才的例子。

如果没有 MQ,订单服务可能需要直接调用:

积分服务
短信服务
优惠券服务
物流服务
推荐系统
数据分析系统

也就是说,订单服务需要知道:

积分服务在哪?
短信接口是什么?
优惠券接口是什么?
调用失败怎么办?

如果以后又增加一个“用户成长值系统”,订单服务还需要继续修改代码:

add_growth_value()

随着业务越来越多,订单服务和其他系统之间的依赖也会越来越复杂。


引入 MQ 后,订单服务只负责一件事:

告诉 MQ:订单创建成功了

例如发送:

{
"order_id": 10001,
"user_id": 9527,
"event": "ORDER_CREATED"
}

至于谁需要这个消息,订单服务并不关心。

订单服务

MQ

┌─┼───────────────┐
↓ ↓ ↓
积分服务 短信服务

优惠券服务

以后新增一个数据分析系统,只需要让它订阅这个消息即可:

订单服务

MQ

数据分析系统

订单服务本身甚至不需要修改。

这就是:

解耦

也就是:

消息发送方只负责发送消息,不需要关心消息最终由谁处理。

这样不同系统之间的依赖就会减少。


4. 削峰:MQ 为什么能扛住突然的大量请求?

相比异步和解耦,削峰通常更容易让人困惑。

假设订单系统最多只能稳定处理:

1000 个请求 / 秒

平时访问量只有:

300 个请求 / 秒

完全没有问题。

但是到了秒杀活动开始的一瞬间:

10000 个请求 / 秒

突然全部进入订单系统。

就可能出现:

CPU 飙升
数据库连接池耗尽
大量请求超时
甚至服务直接崩溃

整个流量类似:

正常情况:

请求
■■■
■■■
■■■

秒杀开始:

■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■■■■■■

这种突然出现的巨大流量,就是所谓的:

流量峰值


5. MQ 是怎么把峰削掉的?

加入消息队列以后,请求不再全部直接进入订单系统。

而是:

大量请求

MQ

订单服务慢慢消费

假设瞬间来了:

10000 个请求

MQ 可以先把这些消息存下来。

订单服务的处理能力只有:

1000 条 / 秒

那么它就按照自己的能力消费:

第 1 秒:处理 1000
第 2 秒:处理 1000
第 3 秒:处理 1000

也就是说:

原来:

10000 请求

订单服务

瞬间压垮

变成:

10000 请求

MQ

1000/s

订单服务

MQ 就像在请求和服务之间放了一个巨大的“蓄水池”。


6. 用水库理解削峰

其实可以把 MQ 想象成一个水库。

突然下暴雨:

水流量:10000 L/s

但下游河道只能承受:

1000 L/s

如果所有水直接进入下游:

暴雨
↓↓↓↓↓↓↓↓↓
河道

很容易发生洪水。

如果中间修一个水库:

暴雨
↓↓↓↓↓↓↓↓↓
██████████
水库
██████████

1000 L/s

下游

水库先把大量水暂时存起来,然后按照下游能够承受的速度慢慢放水。

MQ 的作用非常类似:

大量请求 = 暴雨

MQ = 水库

业务系统 = 下游河道

因此所谓削峰,并不是:

MQ 让业务系统突然变快了。

而是:

MQ 把瞬间的大量请求暂时存下来,让后端按照自己的处理能力逐渐消费。

这也是理解削峰最关键的一点。


7. 削峰其实是用时间换压力

比如:

10000 条消息

业务系统一秒只能处理:

1000 条

那么使用 MQ 后,大约需要:

10000 ÷ 1000 = 10 秒

才能全部处理完成。

MQ 并没有减少业务量。

原本要处理:

10000 个任务

使用 MQ 后依然需要处理:

10000 个任务

区别只是原来:

1 秒全部压过来

现在变成:

分 10 秒慢慢处理

因此,削峰本质上可以理解为:

把空间上的压力转换成时间上的等待。


8. 异步、解耦、削峰到底有什么区别?

最后再把三者放在一起看。

异步

解决:

主流程等待时间过长

核心思想:

先发送消息
不等待处理完成

例如:

下单成功

MQ

后台发送短信


解耦

解决:

系统之间依赖太强

原来:

订单服务 → 短信服务
订单服务 → 积分服务
订单服务 → 优惠券服务

现在:

订单服务

MQ

各个业务系统自己订阅

发送者和消费者不再直接依赖。


削峰

解决:

瞬时流量过大

核心思想:

大量请求

MQ 暂存

业务系统按照自己的速度处理

MQ 相当于流量缓冲区。


9. MQ 也不是没有代价

既然消息队列这么好,是不是所有业务都应该加 MQ?

当然不是。

引入 MQ 后,系统也会变得更加复杂。

例如你需要考虑:

消息发送失败怎么办?
消息丢失怎么办?
消息重复消费怎么办?
消费者处理失败怎么办?
消息堆积怎么办?
如何保证消息顺序?

原来的系统可能只是:

A → B

加入消息队列以后变成:

A → MQ → B

虽然解决了异步、解耦和削峰的问题,但同时也引入了新的问题。

因此 MQ 更适合:

非核心业务异步处理

多个系统订阅同一个业务事件

瞬时流量较大的场景

允许一定时间延迟的业务

而对于一些简单、强实时的小型业务,直接同步调用反而更加简单。

赞(0)
未经允许不得转载:171主机测评 » 消息队列为什么能削峰?异步、解耦、削峰到底是什么意思
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址