究竟什么时候该使用MQ?

简介: MQ,互联网技术体系中一个常见组件,究竟什么时候不使用MQ,究竟什么时候使用MQ,MQ究竟适合什么场景,是今天要分享的内容。

脱离业务的组件引入都是耍流氓。引入一个组件,最先该解答的问题是,此组件解决什么问题。

MQ,互联网技术体系中一个常见组件,究竟什么时候不使用MQ,究竟什么时候使用MQ,MQ究竟适合什么场景,是今天要分享的内容。

MQ是什么?消息总线(Message Queue),后文称MQ,是一种跨进程的通信机制,用于上下游传递消息。画外音:这两个进程,一般不在同一台服务器上。
image.png

在互联网架构中,MQ经常用做“上下游解耦”:(1)消息发送方只依赖MQ,不关注消费方是谁;(2)消息消费方也只依赖MQ,不关注发送方是谁;

画外音:发送方与消费方,逻辑上和物理上都不依赖彼此。


什么时候不使用MQ? 当调用方需要关心消息执行结果时,通常不使用MQ,而使用RPC调用。
image.png

ret = PassportService::userAuth(name, pass);

switch(ret){

    case(YES) : return YesHTML();

    case(NO) : return NoHTML();

    case(JUMP) : return 304HTML():

    default : return 500HTML();

}

  如上例所示,上游调用Passport服务,处理结果不同,业务会走不同的逻辑处理分支(登录成功,登录失败,执行错误等),即“处理结果强依赖”,此时应该使用RPC调用。 画外音:绝大部分情况,应该使用RPC。   此时如果强行使用MQ呢?
image.png

如果强行使用MQ通讯,调用方不能直接告之用户登录成功又或失败,则要等待另一个MQ通知回调。这么玩,不但使得编码复杂,还会引入消息丢失的风险,中间多加入一层,多此一举。

究竟什么时候使用MQ呢?

下面四类典型场景,应该使用MQ。
典型场景一:数据驱动的任务依赖
什么是任务依赖? 举个栗子,互联网公司经常在凌晨进行一些数据统计任务,这些任务之间有一定的依赖关系,例如: (1)task3需要使用task2的输出作为输入; (2)task2需要使用task1的输出作为输入; 这样的话,tast1, task2, task3之间就有任务依赖关系,必须task1先执行,再task2执行,载task3执行。
对于这类需求,通常怎么实现呢? 常见的玩法是,crontab人工排执行时间表
image.png

如上图,手动设定如下:

(1)task1,0:00执行,经验执行时间为50分钟; (2)task2,1:00执行(为task1预留10分钟buffer),经验执行时间也是50分钟; (3)task3,2:00执行(为task2预留10分钟buffer);   crontab手动排表有什么坏处呢? (1) 如果有一个任务执行时间超过了预留buffer的时间,将会得到错误的结果 ,因为后置任务不清楚前置任务是否执行成功,此时要手动重跑任务,还有可能要调整排班表; (2) 总任务的执行时间很长 ,总是要预留很多buffer,如果前置任务提前完成,后置任务不会提前开始; (3)如果一个任务被多个任务依赖,这个任务将会称为关键路径, 排班表很难体现依赖关系,容易出错 (4) 如果有一个任务的执行时间要调整,将会有多个任务的执行时间要调整

无论如何,采用“crontab排班表”的方法,各任务严重耦合,谁用过谁痛谁知道。 画外音:用过的,痛过的,请留言。  
应该如何优化呢? 采用MQ解耦。
image.png

如上图,任务之间通过MQ来传递“开始”与“结束”的通知:

(1)task1准时开始,结束后发一个“task1 done”的消息; (2)task2订阅“task1 done”的消息,收到消息后第一时间启动执行,结束后发一个“task2 done”的消息; (3)task3同理;   采用MQ有什么好处呢? (1) 不需要预留buffer ,上游任务执行完,下游任务总会在第一时间被执行; (2)依赖多个任务,被多个任务依赖都很好处理, 只需要订阅相关消息即可 (3) 有任务执行时间变化,下游任务都不需要调整执行时间   需要特别说明的是,MQ只用来传递上游任务执行完成的消息,并不用于传递真正的输入输出数据   典型场景二:上游不关心执行结果
上游需要关注执行结果时要用“RPC调用”,上游不关注执行结果时,使用MQ。   举个栗子,58同城的很多下游需要关注“用户发布帖子”这个事件,比如: (1)招聘用户发布帖子后,招聘业务要奖励58豆; (2)房产用户发布帖子后,房产业务要送2个置顶; (3)二手用户发布帖子后,二手业务要修改用户统计数据;  
对于这类需求,可以采用什么方式实现呢?
image.png

比较无脑的,可以使用RPC调用来实现:

帖子发布服务执行完成之后,调用下游招聘业务、房产业务、二手业务,来完成消息的通知。
但事实上,这个通知是否正常正确的执行,帖子发布服务根本不关注。   通过RPC来传递不需要知道处理结果的通知,有什么坏处呢? (1)帖子发布流程的执行时间增加了; (2)下游服务当机,可能导致帖子发布服务受影响,上下游逻辑+物理依赖严重; (3)每当增加一个需要知道“帖子发布成功”信息的下游,修改代码的是帖子发布服务; 画外音:这一点是最恶心的,属于架构设计中典型的反向依赖。
如何来进行优化呢? 采用MQ解耦,代替RPC。
image.png

如上图所示:

(1)帖子发布成功后,向MQ发一个消息;2)哪个下游关注“帖子发布成功”的消息,主动去MQ订阅;   如此一来,有什么好处呢? (1)上游执行时间短; (2)上下游逻辑+物理解耦,除了与MQ有物理连接,模块之间都不相互依赖; (3)新增一个下游消息关注方,上游不需要修改任何代码;   典型场景三:上游关注执行结果,但执行时间很长
有时候上游需要关注执行结果,但执行结果时间很长(典型的是调用离线处理,或者跨公网调用),也经常使用 回调网关+MQ 来解耦。   举个栗子,网上支付,跨公网调用的接口,执行时间会比较长,但调用方又非常关注执行结果,此时一般怎么玩呢?
image.png

一般采用“回调网关+MQ”方案来解耦:

(1)调用方直接跨公网调用支付接口; (2)程序返回调用成功,此时并不代表返回成功; (3)程序执行完成后,回调统一网关; (4)网关将返回结果通知MQ; (5)请求方收到结果通知;   这里需要注意的是, 不应该由回调网关来RPC通知上游来通知结果 ,如果是这样的话,每次新增调用方,回调网关都需要修改代码,仍然会反向依赖,使用回调网关+MQ的方案,新增任何对支付的调用,都不需要修改代码。   结尾总结 MQ是一个互联网架构中常见的解耦利器。


什么时候不使用MQ? 上游实时关注执行结果,通常采用RPC。   什么时候使用MQ? (1)数据驱动的任务依赖; (2)上游不关心多下游执行结果; (3)异步返回执行时间长;


知其然,知其所以然 ,希望大家有收获。

本文转自“架构师之路”公众号,58沈剑提供。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
6月前
|
消息中间件 存储 中间件
【消息中间件】详解三大MQ:RabbitMQ、RocketMQ、Kafka
【消息中间件】详解三大MQ:RabbitMQ、RocketMQ、Kafka
1588 0
|
消息中间件 Java 中间件
秒懂消息队列MQ,万字总结带你全面了解消息队列MQ
消息队列是大型分布式系统不可缺少的中间件,也是高并发系统的基石中间件,所以掌握好消息队列MQ就变得极其重要。接下来我就将从零开始介绍什么是消息队列?消息队列的应用场景?如何进行选型?如何在Spring Boot项目中整合集成消息队列。
15975 7
秒懂消息队列MQ,万字总结带你全面了解消息队列MQ
|
4月前
|
消息中间件 Java Kafka
MQ 消息队列 比较
MQ 消息队列 比较
48 0
|
7月前
|
消息中间件 大数据 Java
消息队列 MQ
消息队列 MQ
67 3
|
消息中间件 Java Spring
消息中间件MQ
个人理解
87 0
|
消息中间件 存储 Java
MQ 介绍|学习笔记
快速学习 MQ 介绍
398 0
MQ 介绍|学习笔记
|
消息中间件 存储 Java
MQ系列5:RocketMQ消息的发送模式
MQ系列5:RocketMQ消息的发送模式
420 0
MQ系列5:RocketMQ消息的发送模式
|
消息中间件 监控 Linux
MQ-MQ的安装
MQ-MQ的安装
259 0
|
消息中间件 前端开发 大数据
MQ学习-MQ是什么?
MQ学习-MQ是什么?
587 0
|
消息中间件 前端开发 大数据
MQ简单介绍
MQ(message queue),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已,还是一种跨进程的通信机制,用于上下游传递消息。