高中的时候,每节自习课都会有人零零散散的找老师问问题,一开始就一两人还好,后来渐渐的人多了,老师也烦了,你说我这上了一天的课难得晚上可以看自习休息会,这帮小崽子还一个个这么折腾人。
于是老师决定让同学们把需要提的问题写在纸上,下自习后交给课代表,然后老师再从课代表那取出要问的问题,然后再一个个解决。
相信在学生时代大家都遇到过上面的这种情况,如果我们将在学校上课抽象成一个系统,那这种情况就是一个很常见的消息队列的使用场景。
在上述实例中,要提的问题就是 「消息」,提问题的学生是 「生产者」,回答问题的老师是 「消费者」,收集问题的课代表是 「消息队列」。
看到「生产者」和「消费者」,不知道有没有想起来在 RPC 的中同样也有生产者和消费者,那么这两者之间有关联吗?
如果对 RPC 还不了解的读者可以阅读我之前写过的一篇文章
我们先来回顾一下,在 RPC 的调用关系里,我们把提供具体的调用方法的系统叫服务提供者(Provider),调用服务的系统称为服务消费者(Consumer)。
如果有 N 个不同系统相互之间都有 RPC 调用,这时候整个系统环境就是一个很大的网状结构,如下图所示,依赖关系有 N*(N-1)/2 个。任何一个系统出问题,都会影响剩下 N-1 个系统,这种强耦合肯定不是我们想要的,那应该怎么降低这种耦合呢?
基于这些问题,消息队列(Message Queue,简称 MQ)技术横空出世,所有的处理请求先作为一个消息发送到 MQ(一般我们叫做 broker),接着处理消息的系统从 MQ 拿到消息并进行处理。这样就实现了各个系统间的解耦,同时可以把失败策略、重试等作为一个机制,对各个应用透明,直接在 MQ 与各调用方的应用接口层面实现即可,如下图所示:
一般来说,我们把发送消息的系统称为消息生产者(message producer),接受处理消息的系统称为消息消费者(message consumer)。
一般来说,我们把发送消息的系统称为消息生产者(message producer),接受处理消息的系统称为消息消费者(message consumer)。
根据消息处理的特点,我们又可以总结两种消息模式:
- 点对点模式(Point to Point,PTP),一个生产者发送的每一个消息,都只能有一个消费者能消费,看起来消息就像从一个点传递到了另外一个点。
- 发布订阅模式(Publish-Subscribe,PubSub),一个生产者发送的每一个消息,都会发送到所有订阅了此队列的消费者,这样对这个消息感兴趣的系统都可以拿到这个消息。
使用 MQ 的好处
从上面的介绍中我们可以看出 MQ 的出现在一定程度上是可以将复杂的服务解耦。
那么回到一开始的例子,我们发现,课代表的出现也确实在一定程度上解决了老师和学生之间的强耦合关系。类似的例子还有快递柜,快递员将快递放到快递柜,不方便拿快递的人可以在下班之后再去拿快递。这样快递员和收快递的人就可以更加自主,只通过一个简单的“容器”——快递柜来联系。你甚至可以不知道你的快递员长什么样子,快递员也可以不知道你是谁,在他们眼里,都只有快递柜,没有对方。
毫无疑问,与一个简单的容器打交道,比与复杂的人打交道容易一万倍,他们也可以更加专注的去完成自己的事情。
那么除解耦外还有其他的优势吗?自然是有的。
再回到一开始学生提问的例子,我们设想另一个场景:如果提问的问题特别难,而且是每个人都要问这个问题,老师给每个人讲解要花很多时间,学生有压力,老师也会觉得麻烦,所以老师决定第二天上课的时候把这个问题单独拿出来说。
在上面这个事例中,就体现了 MQ 的另外两个作用——广播和削峰。
广播:老师只需要讲解一次,所有需要听这道题的同学就都可以听了。
削峰:将当晚那么多同学讲题的需求延迟到第二天上课。
当然,在实际的使用过程中,MQ 的用处也不只是这么些,这里推荐阅读阿里巴巴中间件团队的十分钟入门 RocketMQ 中的第一章 👉消息中间件需要解决哪些问题?
MQ 的使用成本
如此一看,消息队列确实有很多好处,不过凡事皆有利弊,消息队列的使用也是有一定的使用成本的。
引入了一定的复杂度
虽然引入了消息队列可以让各个系统解耦,但同时也增加了维护存放消息的容器的成本。
就和例子中的快递柜一样,维护快递柜也是需要成本的,不然前段时间也不会出现丰巢收费的事情了。当过课代表的人也知道自己有多辛苦。
暂时的不一致性
同学问问题,老师没办法立即给出解答。快递到了,你不能及时感受拆快递的快感,这些在一个复杂系统中体现出来的就是暂时的不一致性。
不过,就像老话说的,正义可能会迟到,但永远不会缺席。老师最终还是会给你解决问题,你最终还是可以感受到拆快递的快感,这就是所谓的「最终一致性」。
什么情况下适合使用 MQ?
容忍暂时的不一致性
如果不是对消息的处理非常及时,比如电商系统中的付款和下订单以及物流配送,只要用户最终满意于「尽快收到货」,这种情况就可以使用 MQ。
当然,如果用户对这种情况无法容忍,那就再想想其他办法吧。
收益>成本
这点也很好理解,如果使用维护一个消息队列的成本,比消息队列对项目的实际效益要大,那使用他作甚?
以上就是本篇文章的全部内容了,其实本来想在最后写一点关于利用 docker 部署 RocketMQ 的,但因为最近实习+实验室的工作真的是太多了,精力实在有限,所以这一部分只能留到以后来说了。 如果你觉得我的文章对你有帮助,不妨关注一下,我会尽可能分享更多优质内容。