异步架构的主要组成部分:消息生产者、消息消费者、分布式消息队列。
异步架构的两种主要模型:点对点模型和订阅模型
消息队列构建异步调用框架
消息的生产者将消息送到消息队列以后,由消息的消费者从消息队列中获取消息,然后进行业务逻辑的处理,消息的生产者和消费者是异步处理的,彼此不会等待阻塞,所以叫做异步架构。
一、使用消息队列构建一个异步调用架构,需要3个角色:一是消息的生产者,二是消息队列,三是消息的消费者。
消息的生产者是客户端应用程序代码的一部分,用来初始化异步调用处理流程。在消息队列的处理中,生产者职责比较少,它主要做的就是创建一个合法的消息,并把这个消息发送到消息队列中。
消息队列,是异步架构中的一个重要的组成部分,消息队列是消息发送的目的地,也是发给消费者过程中的一个缓冲。消息队列实现方式有很多种,可以是共享文件夹、也可以是关系型数据库或者非关系型数据库,最主要的还是使用专门的分布式消息队列服务器。
消息消费者,消息的消费者从消息队列中接收并处理消息,是一个异步处理的组件。消息的消费者不需要知道生产者的存在,它只依赖消息队列中的消息。消息的消费者通常部署在独立的服务器上,和消息的生产者完全隔离。可以通过添加硬件的方式伸缩。
二、点对点模型和发布订阅模型
1、点对点模型,消费者和生产者只需要知道消息队列的名字,生产者发送消息到消息队列中,而消息队列的另一端是多个消费者竞争消费消息,每个到达消息队列的消息只会被路由到一个消费者中去,所以消费者看到的是全部消息的一个子集。消息的生产者有多个,消息的消费者也有多个,多个生产者将消息发送到消息队列中,而有多个消费者去消息队列中对消息进行竞争行的消费。每条信息只会被一个消费者消费,每个消费者只会消费消息队列中的一部分消息。
2、发布订阅模型,消息可能被发送到多个消费者,生产者发送消息到一个主题,而不是队列了,这个主题被克隆给每一个订阅他的消费者,每个消费者接收一份消息复制到自己的私有的消息队列中,消费者可以独立于其他消费者使用自己订阅的消息,消费者之间不会存在竞争关系,通常分布式消息队列使用的就是发布订阅模型。
两种模型对比
通常点对点模型,适用于耗时较长、逻辑相对独立的业务,比如发送邮件这个操作。因为发送邮件比较耗时,程序也不关心邮件发送是否成功,发送邮件的逻辑相对独立,所以只需要把邮件消息丢到消息队列中就可以返回了。消费者也不用关系是哪个生产者发送的邮件。只需要把邮件消息内容取出来以后进行消费,通过远程服务器邮件发送出去就可以。而且每个邮件只需要被发送一次,所以消息只被一个消费者消费就可以了。
对于另外的场景,比如注册新用户,就比较适合发布订阅模型。一个新用户注册后,需要给用户发送一个激活邮件,发送一条欢迎短信。还需要将用户注册数据写入数据库,甚至需要将新用户信息发送给关联企业的系统。那么对于一个新注册的用户这样的消息,就适合用订阅发布消息,一个新用户注册,会把注册消息发送给一个主题,多个消费者可以订阅这个主题,比如发送邮件的消费者、发送短信的消费者、将注册信息写入数据库的消费者,跨系统同步消息的消费者。
三、消息队列的好处,包括异步处理、易伸缩、削峰填谷、失败隔离及自我修复、解耦。
1、异步处理,提升处理性能,对一些比较耗时的操作,我们可以把处理过程通过消息队列进行异步处理。这样可以推迟耗时操作的处理,使耗时操作异步化,而不必阻塞客户端的程序,客户端的程序在得到处理结果之前就可以就行执行,从而提高程序的处理性能。
2、易伸缩,它可以让系统具有更好的伸缩性,因为耗时的任务可以通过分布式消息队列向多台消费者服务器并行发送消息,然后在很多台消费者服务器上并行处理消息,也就是说在多台物理服务器上运行消费者,那么当负载上升的时候,就很容易的添加更多的机器成为消费者。
3、削峰填谷,使用消息队列即便访问流量持续的增长,系统依然可以持续的接收请求。这种情况下,虽然生产者发布消息的速度比消费者消费消息的速度快,但是可以持续的将消息纳入到消息队列中,用消息队列作为消息的缓冲,因此短时间,发布者不会受到消费处理能力的影响。
4、失败隔离及自我修复,因为发布者不直接依赖消费者,所以分布式消息队列可以将消费者系统产生的错误异常与生产者系统隔离开来,生产者不受消费者失败的影响。当在消息消费过程中出现处理逻辑失败的时候,这个错误只会影响到消费者自身,而不会传递给消息的生产者,也就是应用程序可以按照原来的处理逻辑继续执行。
5、解耦,使用分布式消息队列,可以使生产者和消费者的代码实现解耦,也就是说多个生产者发布消息,多个消费者处理消息,共同完成完整的业务处理逻辑,但是它们却不需要直接进行交互调用,没有代码的依赖耦合。
四、同时分布式消息队列也会遇到一些挑战:消息无序、消息重新入队列、竞态条件。
1、消息无序,因为生产者和消费者是异步处理的,虽然消息队列本身会保证先创建的消息在前面,但是消费者却不能保证先创建的消息先消费。最简单的解决办法是将消息处理的顺序设计到异步流程中,也就是创建用户的消费者在处理消息后,在发送一个欢迎邮件的消息到消息队列中。
2、消息重新入队,消息重新入队列,重复消费,就会导致同一条消息被多次消费。解决方式是将消息处理设计成幂等性。
3、竞态条件,是指在程序并发执行的时候,不同的执行顺序会导致不同的结果,主要是因为对共享资源的访问顺序不同导致的结果不同,我们在编程中通过多线程实现程序的并发执行,消息队列可以在分布的环境下实现架构层面的并发执行,并发执行可以可能会导致对资源的争用。在编程中可以使用锁的机制进行并发的控制,以避免竞态、顺序执行。在消息队列的异步架构中也需要对共享资源的并发访问进行控制,以避免竞态条件的出现。