任何脱离业务的组件引入都是耍流氓. 引入一个组件, 最先该解答的问题是, 此组件解决什么问题.
MQ, 互联网技术体系中一个常见组件, 究竟什么时候不使用 MQ, 究竟什么时候使用 MQ,MQ 究竟适合什么场景, 是今天要分享的内容.
MQ 是什么? 消息总线 (Message Queue), 后文称 MQ, 是一种跨进程的通信机制, 用于上下游传递消息. 画外音: 这两个进程, 一般不在同一台服务器上.
在互联网架构中, MQ 经常用做 "上下游解耦":(1) 消息发送方只依赖 MQ, 不关注消费方是谁;(2)消息消费方也只依赖 MQ, 不关注发送方是谁;
画外音: 发送方与消费方, 逻辑上和物理上都不依赖彼此.
什么时候不使用 MQ? 当调用方需要关心消息执行结果时, 通常不使用 MQ, 而使用 RPC 调用
- 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 呢 如果强行使用 MQ 通讯, 调用方不能直接告之用户登录成功又或失败, 则要等待另一个 MQ 通知回调. 这么玩, 不但使得编码复杂, 还会引入消息丢失的风险, 中间多加入一层, 多此一举. 究竟什么时候使用 MQ 呢? 下面四类典型场景, 应该使用 MQ.
典型场景一: 数据驱动的任务依赖
什么是任务依赖? 举个栗子, 互联网公司经常在凌晨进行一些数据统计任务, 这些任务之间有一定的依赖关系, 例如:(1)task3 需要使用 task2 的输出作为输入;(2)task2 需要使用 task1 的输出作为输入; 这样的话, tast1, task2, task3 之间就有任务依赖关系, 必须 task1 先执行, 再 task2 执行, 载 task3 执行.
对于这类需求, 通常怎么实现呢? 常见的玩法是, crontab 人工排执行时间表.
如上图, 手动设定如下:(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 解耦.
如上图, 任务之间通过 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)二手用户发布帖子后, 二手业务要修改用户统计数据;
对于这类需求, 可以采用什么方式实现呢?
比较无脑的, 可以使用 RPC 调用来实现: 帖子发布服务执行完成之后, 调用下游招聘业务, 房产业务, 二手业务, 来完成消息的通知.
但事实上, 这个通知是否正常正确的执行, 帖子发布服务根本不关注. 通过 RPC 来传递不需要知道处理结果的通知, 有什么坏处呢?(1)帖子发布流程的执行时间增加了;(2)下游服务当机, 可能导致帖子发布服务受影响, 上下游逻辑 + 物理依赖严重;(3)每当增加一个需要知道 "帖子发布成功" 信息的下游, 修改代码的是帖子发布服务; 画外音: 这一点是最恶心的, 属于架构设计中典型的反向依赖.
如何来进行优化呢? 采用 MQ 解耦, 代替 RPC.
如上图所示:(1)帖子发布成功后, 向 MQ 发一个消息;(2)哪个下游关注 "帖子发布成功" 的消息, 主动去 MQ 订阅; 如此一来, 有什么好处呢?(1)上游执行时间短;(2)上下游逻辑 + 物理解耦, 除了与 MQ 有物理连接, 模块之间都不相互依赖;(3)新增一个下游消息关注方, 上游不需要修改任何代码; 典型场景三: 上游关注执行结果, 但执行时间很长
有时候上游需要关注执行结果, 但执行结果时间很长 (典型的是调用离线处理, 或者跨公网调用), 也经常使用回调网关 + MQ 来解耦. 举个栗子, 微信支付, 跨公网调用微信的接口, 执行时间会比较长, 但调用方又非常关注执行结果, 此时一般怎么玩呢?
一般采用 "回调网关 + MQ" 方案来解耦:(1) 调用方直接跨公网调用微信接口;(2)微信返回调用成功, 此时并不代表返回成功;(3)微信执行完成后, 回调统一网关;(4)网关将返回结果通知 MQ;(5)请求方收到结果通知; 这里需要注意的是, 不应该由回调网关来 RPC 通知上游来通知结果, 如果是这样的话, 每次新增调用方, 回调网关都需要修改代码, 仍然会反向依赖, 使用回调网关 + MQ 的方案, 新增任何对微信支付的调用, 都不需要修改代码. 结尾总结 MQ 是一个互联网架构中常见的解耦利器. 什么时候不使用 MQ? 上游实时关注执行结果, 通常采用 RPC. 什么时候使用 MQ?(1)数据驱动的任务依赖;(2)上游不关心多下游执行结果;(3)异步返回执行时间长;
来源: http://www.bubuko.com/infodetail-3357147.html