一、直接说答案
什么时候不使用MQ?
上游实时关注执行结果
什么时候使用MQ?
1)上游不关心多下游执行结果
2)异步返回执行时间长
3)前后有依赖的任务编排
二、MQ是干嘛的
消息队列(Message Queue),后文称MQ,是一种跨进程的通信机制,用于上下游传递消息。
在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。
使用了MQ之后,消息发送上游只需要依赖MQ,逻辑上和物理上都不用依赖其他服务。
三、什么时候不使用消息总线
既然MQ是互联网分层架构中的解耦利器,那所有通讯都使用MQ岂不是很好?这是一个严重的误区,调用与被调用的关系,是无法被MQ取代的。
MQ的不足是:
1)系统更复杂,多了一个MQ组件
2)消息传递路径更长,延时会增加
3)消息可靠性和重复性互为矛盾,消息不丢不重难以同时保证
4)消息链路追踪会更复杂,异常排查的难度会提升
5)上游无法实时知道下游的执行结果,这一点是很致命的
举个栗子:订单创建场景,创建订单时需要检查商品库存,有无库存将直接影响结果,故此处下单的场景不能使用MQ通信,后续文章会逐步再深入解析,其实下单时如库存扣减、积分锁定等与业务结果强依赖的场景不能使用MQ,但一些流水单据、日志记录还是能用用MQ来处理。
无论如何,记住这个结论:调用方实时依赖执行结果的业务场景,一定是上游直接调用下游,而不是使用MQ。
四、什么时候使用MQ
【典型场景一:前后依赖的任务编排】
什么是前后依赖,举个栗子,互联网公司经常在凌晨进行一些数据统计任务,这些任务之间有一定的依赖关系,比如:
1)task3需要使用task2的输出作为输入
2)task2需要使用task1的输出作为输入
这样的话,tast1, task2, task3之间就有任务依赖关系,必须task1先执行,再task2执行,再task3执行。
对于这类需求,常见的实现方式是,使用cron人工排执行时间表:
1)task1,0:00执行,经验执行时间为50分钟
2)task2,1:00执行(为task1预留10分钟buffer),经验执行时间也是50分钟
3)task3,2:00执行(为task2预留10分钟buffer)
这种方法的坏处是:
1)如果有一个任务执行时间超过了预留buffer的时间,将会得到错误的结果,因为后置任务不清楚前置任务是否执行成功,此时要手动重跑任务,还有可能要调整排班表
2)总任务的执行时间很长,总是要预留很多buffer,如果前置任务提前完成,后置任务不会提前开始
3)如果一个任务被多个任务依赖,这个任务将会称为关键路径,排班表很难体现依赖关系,容易出错
4)如果有一个任务的执行时间要调整,将会有多个任务的执行时间要调整
无论如何,采用“cron排班表”的方法,各任务耦合,用过的人都说掉了很多头发。
优化方案是,采用MQ解耦:
1)task1准时开始,结束后发一个“task1 done”的消息
2)task2订阅“task1 done”的消息,收到消息后第一时间启动执行,结束后发一个“task2 done”的消息
3)task3同理
采用MQ的优点是:
1)不需要预留buffer,上游任务执行完,下游任务总会在第一时间被执行
2)依赖多个任务,被多个任务依赖都很好处理,只需要订阅相关消息即可
3)有任务执行时间变化,下游任务都不需要调整执行时间
需要特别说明的是,MQ只用来传递上游任务执行完成的消息,并不必须要传递真正的输入输出数据。
【典型场景二:上游不关心执行结果】
上游需要关注执行结果时要用“调用”,上游不关注执行结果时,就可以使用MQ了。
举个栗子,订单支付完成后很多下游需要关注“订单支付完成”这个事件,比如CRM系统需要根据订单信息赠送会员积分,微信端需要收到通知给用户发送消费通知,O2O订单需要通知业务人员备货,独立的分析系统需要接收变更数据。
对于这类需求,常见的实现方式是,使用调用关系:
订单支付完成后,调用下游CRM的订单推送、微信端的消息通知、POS端的订单下发、分析系统的订单推送。
这种方法的坏处是:
1)订单的支付流程的执行时间增加了
2)下游服务crash了,可能导致订单支付服务受影响,上下游逻辑+物理依赖严重
3)每当增加一个需要知道“订单支付完成”信息的下游,修改订单的支付服务,这一点是最恶心的,属于架构设计中典型的依赖倒转,用过的人都说掉了很多头发。
优化方案是,采用MQ解耦:
1)订单支付完成后,向MQ发一个消息
2)哪个下游关注“订单支付完成”的消息,主动去MQ订阅该事件
采用MQ的优点是:
1)上游执行时间短
2)上下游逻辑+物理解耦,除了与MQ有物理连接,模块之间都不相互依赖
3)新增一个下游消息关注方,上游不需要修改任何代码
典型场景三:上游关注执行结果,但执行时间很长
有时候上游需要关注执行结果,但执行结果时间很长(典型的是调用离线处理,或者跨公网调用),也经常使用回调网关+MQ来解耦。
举个栗子,支付宝支付,跨公网调用微信的接口,执行时间可能会比较长,但调用方又非常关注执行结果,此时一般怎么玩呢?
一般采用“回调网关+MQ”方案来解耦:
1)调用方直接跨公网调用支付宝接口
2)支付宝返回调用成功,此时并不代表支付成功
3)支付宝执行完成后,回调统一网关
4)网关将返回结果通知MQ
5)请求方收到结果通知
这里需要注意的是,不应该由回调网关来调用上游来通知结果,如果是这样的话,每次新增调用方,回调网关都需要修改代码,仍然会反向依赖,使用回调网关+MQ的方案,新增任何对支付宝支付的调用,都不需要修改代码。
五、总结
MQ是一个互联网架构中常见的解耦利器。
但一切脱离业务的架构设计与新技术引入都是耍流氓。
引入MQ之前,架构师们还是首先要考虑清楚,用这个技术来解决什么问题。
希望以上分享能给程序员、架构师在架构设计上有帮助,不足之处,请批评指正,欢迎一起交流讨论。