概述
消息队列服务利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下提供应用解耦、弹性伸缩、冗余存储、流量削峰、异步通信、数据同步等等功能,其作为分布式系统架构中的一个重要组件,有着举足轻重的地位。

消息的生产者将消息发送到消息队列以后,由消息的消费者从消息队列中获取消息,然后进行业务逻辑的处理,消息的生产者和消费者是异步处理的,彼此不会等待阻塞,所以也称作异步架构。
消息队列架构及原理
消息队列由3 个角色组成:一是消息的生产者,二是消息队列,三是消息的消费者:

1.消息生产者
消息的生产者是客户端应用程序代码的一部分,用来初始化异步调用处理流程。在消息队列的处理中,生产者的职责非常少,它要做的就是创建一个合法的消息,并把这个消息发送到消息队列中,由应用开发者决定生产者的代码在哪里执行,什么时候发送消息。
2.消息队列
消息队列是消息发送的目的地,也是发给消费者过程中的一个缓冲。消息队列实现的方法有好多种,可以用共享文件夹,也可以用关系数据库或者 NoSQL 系统,当然最主要的还是使用专门的分布式消息队列服务器。
3.消息消费者
消息的消费者从消息队列中接收并处理消息,也是由应用开发者实现的,但是一个异步处理的组件。消息的消费者不需要知道生产者存在,它只依赖消息队列中的消息。
4.点对点模型
点对点模型:消费者和生产者只需要知道消息队列的名字,生产者发送消息到消息队列中,而消息队列的另一端是多个消费者竞争消费消息,每个到达消息队列的消息只会被路由到一个消费者中去,所以消费者看到的是全部消息的一个子集。

消息的生产者有多个,消息的消费者也有多个,多个生产者将消息发送到消息队列中,而有多个消费者去消息队列中对消息进行竞争性的消费。每条消息只会被一个消费者消费,每个消费者只会消费消息队列中的一部分消息。
5.发布订阅模型
在发布订阅模型中,消息可能被发送到不止一个消费者,生产者发送消息到一个主题,而不是队列中。消息被发布到主题后,就会被克隆给每一个订阅它的消费者,每个消费者接收一份消息复制到自己的私有队列。消费者可以独立于其他消费者使用自己订阅的消息,消费者之间不会竞争消息。

6.两种模式应用场景
通常使用点对点模型的,是一些耗时较长的、逻辑相对独立的业务。消费者也不需要关心是哪个生产者去发送的消息,它只需要把消息内容取出来以后进行消费,通过远程服务器将消息发送出去就可以了。而且每个消息只需要被发送一次,因此消息只被一个消费者消费就可以了。
发布订阅模型适合另一些场景,比如新用户注册。一个新用户注册成功以后,需要给用户发送一封激活邮件,发送一条欢迎短信,还需要将用户注册数据写入数据库,甚至需要将新用户信息发送给关联企业的系统,这样允许用户可以一次注册就能登录使用多个关联产品。那么对于新用户注册这样一个消息,会把注册消息发送给一个主题,多种消费者可以订阅这个主题,比如发送邮件的消费者、发送短信的消费者、将注册信息写入数据库的消费者,跨系统同步消息的消费者等。
消息队列的好处
综上所述,消息队列有这些优点:包括异步通信、弹性伸缩、削峰填谷、失败隔离及自我修复、解耦。
1.异步通信
对一些比较耗时的处理过程,可以通过消息队列进行异步处理。这样做的好处是推迟耗时操作的处理,不必阻塞客户端的程序,客户端的程序在得到处理结果之前就可以继续执行,从而提高客户端程序的处理性能。
2.弹性伸缩
耗时的任务可以通过消息队列向多台消费者服务器并行发送消息,然后在很多台消费者服务器上并行处理消息。那么当负载上升的时候,可以很容易地添加更多的机器成为消费者。
3.削峰填谷
消息队列还可以平衡流量峰值,进行削峰填谷。
在访问高峰,用户的并发访问数可能超过了系统的处理能力,所以在高峰期就可能会导致系统负载过大,响应速度变慢,更严重的可能会导致系统崩溃。这种情况下,通过消息队列将用户请求的消息纳入到消息队列中,通过消息队列缓冲消费者处理消息的速度。而在低谷期它也还是使用自己最大的处理能力去获取消息,将前面缓冲起来、来不及及时处理的消息处理掉。那么,通过这种手段可以实现系统负载削峰填谷,也就是说将访问的高峰削掉,而将访问的低谷填平,使系统处在一个最佳的处理状态之下,不会对系统的负载产生太大的冲击。
4.失败隔离及自我修复
由于生产者不直接依赖消费者,因此消息队列可以将消费者系统产生的错误异常与生产者系统隔离开来,生产者不会受到消费者失败的影响。
这也就意味着在任何时候都可以对后端的服务器进行重启、添加或删除操作,而不影响生产者的可用性,这样简化了部署和管理服务器的难度。
5.解耦
使用消息队列可以使生产者和消费者的代码实现解耦,也就是说可以多个生产者发布消息,多个消费者处理消息,共同完成完整的业务处理逻辑,但是却不需要直接进行交互调用,没有代码的依赖耦合。
消息队列缺点
消息队列遇到的挑战:消息无序、消息重新入队列、竞态条件、复杂度风险。
1.消息无序
因为生产者和消费者是异步处理的,消息队列本身会保证先创建的消息在前面,但是消费者却并不能保证先创建的消息会被消费掉。
一个简单的解决办法就是将消息处理的顺序设计到异步流程中,从而满足业务逻辑的顺序性要求。
2.消息重新入队列
消息队列产品支持将消费者处理失败的消息重新放入到消息队列中,重新放入到消息队列中的消息有可能是被处理完成了的,也就是表面看起来处理失败,实际上已经处理完成。那么这种情况下就会导致同一条消息被多次消费。
解决这个问题的主要手段是将消息处理设计成幂等性,也就是说消费者可以对同一条消息进行多次处理计算,而不会影响最终的结果。
3.竞态条件
所谓竞态条件就是指在程序并发执行的时候,不同的执行顺序会导致不同的结果,主要是因为对共享资源的访问顺序不同导致的结果不同。因此,在消息队列的异步架构中也需要对共享资源的并发访问进行控制,以避免竞态条件的出现。
4.复杂度风险
消息队列为系统的架构和处理流程带来了复杂性,从而也对系统架构设计能力和架构把控能力提出了更高的挑战和要求。
消息队列的反模式
所谓模式就是指可多次复用的解决方案。当解决方案一次又一次地被证明是成功的,我们就称它为“模式”。
但是如果解决方案经常会带来问题,被认为是错误的,就称之为“反模式”。典型的反模式一开始用起来不错,但是时间越长问题越多。
1.阻塞式调用
有些分布式消息队列产品允许生产者阻塞,也就是生产者发送消息以后,阻塞等待消息队列处理结果,等消费者处理完成返回处理结果以后,继续向下执行。这样就使消息队列成为一个同步的调用模式。
2.耦合生产者和消费者
虽然消息队列将生产和消费者解耦合了,但是不恰当的设计依然会使生产者和消费者产生耦合。
3.缺少坏消息处理
使用消息队列的时候,不能总是假定消息永远正确。对于引发消费者崩溃的消息,应该丢弃而不是重新处理。
常用消息队列产品
目前业界常用的消息队列产品,主要有:RabbitMQ 、ActiveMQ、RocketMQ 、Kafka。
- RabbitMQ 的主要特点是性能好,社区活跃,但是 RabbitMQ 用 Erlang 开发,我们的应用很少用 Erlang,所以不便于二次开发和维护。
- ActiveMQ 影响比较广泛,可以跨平台,使用 Java 开发,对 Java 开发者比较友好。
- RocketMQ 是阿里推出的一个开源产品,也是使用 Java 开发,性能比较好,可靠性也比较高。
- Kafka 是 Linkedin 出品的,专门针对分布式场景进行了优化,因此分布式的伸缩性会比较好。
由此可见,Kafka 因为最初就是针对互联网的分布式、高可用应用场景而设计的,并且在大数据领域得到广泛支持,资料文档更加完善,因此在互联网企业得到更多的应用。
总结
消息队列通过异步通信的架构方式,将生产者和消费者进行隔离。主要的架构模型有两种:点对点模型和发布订阅模型。其中点对点模型,一个消息只会被一个消费者消费;而发布订阅模型,一个消息可以被多个消费者订阅。
消息队列实现的异步架构可以在架构上带来更多的好处。它可以实现业务逻辑的异步处理,从而获得更好性能特性;可以使系统具有更好的伸缩性;可以平衡用户访问流量,实现削峰填谷;还可以隔离失败,并进行自我修复;以及对生产者和消费者进行解耦,使系统拥有更好的扩展和维护能力。
同时需要关注异步消息队列架构带来的挑战:第一个是消息无序,第二个是消息重复处理,第三个是竞态条件,还有一个是系统的复杂度的增加。
总之,消息队列实际上可以带来很多架构上的好处,但是不正确地使用消息队列可能会丧失这些好处。
网友评论