当你刚刚为公司的一个Web应用实现了一个很棒的注册模块。它看起来简洁、高效。在你沾沾自喜的时候,你的leader对你说,现在咱们需要在注册成功后对用户发送一条短信。过了一段时间后,你的leader又对你说,现在咱们需要在注册成功后对用户发送一条邮件,点击邮件中的激活链接后才算是真正的注册成功。又过了一段时间,你的leader又对你说,现在咱们需要在注册成功后对用户发送一条成功赠送金币的迎新消息。又过了一段时间后…
世界唯一不变的就是不断变化。对于刚才的应用场景,如果所有的新增业务逻辑都写在注册模块中,不但会有大量的耦合,也会增加后期维护的成本。
江湖传闻应对这种场景对于MQ来说小菜一碟,那我们来看下MQ是怎么做到的。
MQ英文全称Message Queue,中文翻译为消息总线或消息队列,它是一种跨进程的通信机制,用于上下游传递消息。
在互联网架构中,MQ是一种非常常见的上下游逻辑解耦+物理解耦的消息通信服务。使用了MQ之后,消息发送上游只需要依赖MQ,逻辑上和物理上都不用依赖其他服务。
对于刚开始我们提到的问题,如果使用MQ的话,我们只需要在注册完成后向MQ中发送一条消息;对于后面的迭代模块只需要订阅该消息即可。无论后期新增多少模块都不会对原始的注册模块造成影响。
其实,我们可以把MQ想象成一个邮局,当你把你想要投递的邮件放进邮箱时,MQ最终会把邮件投递给你的收件人,当然了,你的收件人可以是一个人或者是一群人,这个MQ都是支持的。
既然MQ这么厉害,为什么不所有的通讯都使用MQ呢?
因为没有一种技术普适于所有差异化的业务场景。就好比,相对论把牛顿的经典力学扩展到更普适的宇宙范围,上升到亚光速和光速层面,打破了经典力学的局限性,但是,它也肯定有局限性,不然也不会显得与量子力学格格不入!那么,MQ的局限性都有哪些呢?
举个简单的例子,用户的登录,登录页面调用用户中心的登录服务,而登录服务的执行结果将直接影响登录的结果。此处的登录页面和登录服务就必须使用调用关系,而不能使用MQ通信。假如使用了MQ,想想都很酸爽。。
正因为MQ局限性的存在,那么,我们什么情况下不使用MQ呢?
因为其局限性,所以,当调用方实时依赖执行结果的业务场景,请使用调用,而不是MQ。
我们在了解了什么情况下不宜使用MQ的情况下,我们更应该熟悉MQ常用的几种典型场景。
所谓的数据驱动的任务依赖,顾名思义,就是指某一个任务的输入数据依赖于另一个任务的输出数据。
比如,很多公司会在凌晨进行一些数据统计业务,而有时候多个统计业务就会有数据依赖的情况。
对于这类需求,常见的实现方式是使用,cron表达式进行人工排执行时间表。
但是,这种方法有着显著的弊端:
而且,这种方式也是具有很高的耦合性。因此,我们给出的优化方案就是使用MQ。
ps:此处发送的消息可以理解为,确认任务完成的一个标识消息,而不是任务执行完成后的输出数据
综上所述,在数据驱动的任务依赖的场景下,采用MQ具备了以下优点:
在上面已经说过,如果上游需要关注执行结果,则需要使用调用,如果上游不需要关注执行结果,则可以使用MQ。
此处,我们再次回顾下开篇所说的注册模块,注册成功后,短信模块需要发送一条短信通知,邮件模块需要发送一条邮件通知,金币模块需要发送一条赠送金币通知。这里,我们不关心执行结果,只要执行了就是OK的。
如果采用直接调用:
优化后,采用MQ解耦,在注册成功后,向MQ发送一个消息;哪个下游关注注册成功
这个事件,就主动去MQ订阅
采用MQ后,上游执行时间只关注本身业务;除了与MQ有物理连接,上下游模块都不相互依赖;新增一个下游消息关注方,上游不需要修改任何代码。
有时候上游需要关注执行结果,但执行时间很长。典型的是调用离线处理,或者跨公网调用,经常使用回调网关+MQ来解耦。
比如,微信支付,跨公网调用微信的接口。
执行的流程:
这里可能会有个疑问,为什么不由回调网关来调用上游来通知结果呢? 因为,如果这样的话,每次新增调用方,回调网关都需要修改代码,仍然会反向依赖,使用回调网关+MQ的方案,新增任何对微信支付的调用,都不需要修改代码啦。