-->

后端面试38讲_26_23_异步架构如何避免互相依赖的系统间耦合

你好,我是李智慧。

上一篇文章我们讨论过,使用缓冲架构呢可以减少不必要的计算,快速响应用户请求。

但是缓冲只能改善系统的读操作性能,也就是在读取数据的时候可以不从数据源中读取,而是通过缓冲读取以加快数据读取速度。

但是对于写操作,缓冲就无能为力了。

虽然缓冲的写入速度也很快,但是通常情况下,我们不能够把用户提交的数据直接写入缓冲中。

因为缓冲通常被认为是一种不可靠的重储,缓冲通常无法保证数据的持久性和一致性等这些数据重储的基本要求,因此,数据写操作还是需要写入到关系数据库或者nosql数据库中,因此数据库操作通常都比较慢。

那么,如何提高系统的写操作性能呢?此外,两个应用系统之间需要远程传递数据,通常的做法就是直接进行远程调用,使用HTTP或者是其他的IMI方式进行远程调用。

但是这种方式其实是把两个应用耦合起来了,被调用的应用产生了故障或者升级,都有可能会使调用者故障,或者也不得不升级。

这种系统间的耦合情况又该如何避免?能解决以上问题的主要手段,就是使用消息队列的异步架构,有时候呢也被称为世界驱动架构。

消息队列实现异步架构,使目前互联网应用系统中一种典型的架构模式。

所谓异步架构是和同步架构相对应的。

同步架构是说当应用程序调用服务的时候,当前程序需要阻塞,等待服务完成返回服务结果后,才能继续向下执行。

应用程序代码clict code需要发送邮件调用接口服务。

Email service实现了email service接口的SMTP. Email adapter通过SMTP协议与远程服务器通信远程邮件服务器可能有很多邮件在等待发送。

当前邮件可能要等待较长时间才能发送成功。

发送成功后再通过远程通信返回结果跟应用程序。

在这个过程中,当远程服务器发送邮件的时候,应用程序必须阻塞,等待,准确的说是执行应用程序代码的线程被阻塞。

这种阻塞一方面导致线程不能释放被占用的系统资源,导致系统资源不足,影响系统性能。

另一方面呢也导致无法快速给用户返回响应,结果用户体验较差。

此外,如果远程服务器出现异常,这个异常,会传递给应用程序clan code.如果应用程序没有妥善处理好这个异常,就会导致整个请求处理失败。

事实上,在大部分应用场景下,发送邮件是不需要得到发送结果的。

比如用户注册的时候发送账号激活邮件,无论邮件是否发送成功,都可以给用户返回激活。

邮件已发送,请查收。

邮件确认激活。

如果发送失败,只需要提示用户点击重新发送,再次发送邮件即可。

那么,如何使应用程序不阻塞?等待解决方案就是使用消息队列,实现异步架构应用程序。

Click code调用email service的时候呢,email service将调用请求封装成一个邮件,发送消息,发送给消息队列,然后就直接返回了消用程序。

收到返回以后,就可以继续执行快速完成用户响应,释放系统资源而发送给消息队列的邮件发送消息,则会被一个专门的消息队列消费者程序queen consumer消费掉。

这个消费者通过SMTP email adapter调用远程服务器,完成邮件发送。

如果远程服务器处理异常,这个异常,只会传递给消费者程序queen consumer,而不会影响到应用程序。

你在文章中可以看到典型的消息队列异步架构、应息队列异步架构的主要角色呢包括消息生产者、消息队列和消息消消费者。

消息生产者通常就是主应用程序,生产者将调用请求分装成消息,发送给消息队列。

此外,还需要开发一个专门的消息,消费者程序用来从消息队列中获取消费。

消息由消息消费者完成业务逻辑处理。

消息队列的职责就是缓冲。

消息根据消息,消费者消费根据消息,消费方式又可以分为点对点模式和发布订阅模式两种。

在点对点模式中,多个消息,生产者向消息队列发送消息,多个消息,消费者消费消息,每个消息只会被一个消息消费者消费我在文章中画了一张图。

这张图举例的发送邮件的场景,就是一个典型的点对点模式场景。

任何需要发送邮件的应用程序,都可以作为消息生产者向消息队列发送邮件,而通过SMTP协议调用远程服务发送邮件的消息,消费者程序可以部署在多台服务器上。

但是对于任何一个消息,只会被发送给其中一个消费者服务器。

这些服务器可以根据消息的数量动态伸缩,保证邮件能够及时送达。

然果有五台消费者服务器宕机,也不会影响其他消费者处理消息。

发送邮件既不会影响生产者程序正常运行。

而在发布订阅模式中,开发者可以在消息队列中设置主题消息,生产者的消息按照主题进行发送。

而个消息消费者可以订阅同一个主题,每个消费者都可以收到这个主题的消息拷贝。

而在按照自己的业务逻辑分别进行处理计算。

比如说消息生产者向消息队列某个主题发送消息,m多个消息消费者订阅,该主题就会分别收到这个消息。

M典型的场景就是新用户注册。

新用户注册的时候呢,一方面需要发送激活邮件,另一方面可能还需要发送欢迎短信,还可能需要将用户信息同步给关联产品。

当然还需要将用户信息保存到数据库中。

这种场景也可以用点对点模式,由应用程序,也就是消息生产者构造发送邮件的消息发送到邮件消息队列以及构造短信消息、构造新用户消息、构造数据库消息分别发送到相关的消息队列里,然后有对应的消息消费者程序分别获取消息进行处理。

但是,更好的处理方式是使用发布订阅模式。

在消息队列中创建新用户注册主题应用程序。

只需要发布包含新用户注册数据的消息,到该主题中,相关消费者再订阅该主题即可。

不同的消费者都可以订阅该主题,得到新用户注册消息,然后根据自己的业务逻辑,从消息中获取相关的数据进行处理。

在发布订阅模式下一个主题可以被重复订阅。

如果需要扩展功能,可以在对当前的生产者和消费者都没有影响的前提下,增加一个消费者订阅同一个主题,即可使用消息队列,实现异步架构可以解决文章开篇提出的问题,实现更高的写操作性能以及更低的耦合性。

让我们总结一下,使用消息队列的异步架构都有什么好处。

首先就是可以改善写操作请求的响应时间,使用消息队列。

生产者应用程序只需要将消息发送到消息队列之后就可以继续向下执行了,无需等待耗时的消息消费处理。

也就是说啊,可以更快速的完成请求处理操作,快速响应用户。

第二个是更容易进行伸缩。

我在第十篇文章中说过,应用程序也可以通过负载均衡实现集群伸缩。

但是这种集群伸缩是以整个应用服务器为单位的。

如果只是其中某些功能有负载压力,比如用户上传图片,需要对图片进行识别分析,压缩的一些比较耗时的计算操作,也需要伸缩整个应用服务器集群。

事实上呢,图片处理只是应用的一个相对小的功能。

如果因为这个,就对整个应用服务器集群进行伸缩,代价可能会比较大。

如果用消息队列将图片处理相关的操作放署消费者服务器上,那么就可以单独对图片处理的消费者集群进行伸缩。

第三个是消分天股。

互联网应用的访问压力随时都在变化,系统的访问高峰和低谷的并发压力可能会有非常大的差距。

如果按照压力最大的情况部署服务器集群,那么服务器在绝大部分时间内都处于空闲状态。

但是,利用消息队列,我们可以将要处理的消息放入到消息队列中,而消费者可以控制消费速度,因此能够降低系统访问高分时的压力。

而在访问低谷的时候,还可以继续消费消息队列中未处理的消息,保持系统的资源利用率。

第四个就是可以隔离失败,使用消息队列。

生产者发送消息到消息队列后,就继续自己后面的计算。

消费者如果在处理消息的过程中,失败,不会传递给生产者应用程序,具有更高的可用性,最后可以降低耦合。

如我们上面说的发送邮件的例子一样,如果调用是同步的,那么意味着调用者和被调用者必然存在依赖。

一方面是代码上的依赖应用程序,需要依赖发送邮件相关的代码,如果需要修改发送邮件的代码,也必须要修改应用程序。

而且如果要增加新的功能,比如发送短信,也必须要修改应用程序。

另一方面是结果的依赖应用程序必须要等到返回调用结果才能够继续执行。

如果调用出现异常,应用程序必须要处理这个异常。

我们知道耦合会使软件僵硬、笨拙,难以维护。

而使用消息队列的异步架构呢,可以降低调用者和被调用者的耦合。

调用者发送消息到消息队列不需要依赖,被调用者的代码和处理结果增加新的功能,也只需要增加新的消费者就可以了。

消息队列实现异步架构是改善互联网应用写操作性能的一个重要手段,是一种低耦合易扩展的分布式应用架构模式。

但是使用这种架构有些方面也需要注意。

比如,消费者程序可能没有完成用户请求的操作,上面发送的邮件的例子。

消费者程序发送邮件的时候可能会遇到各种问题,从而未完成邮件。

发送邮件的问题还比较简单。

比如,可以提示用户如果未收到邮件,点击按钮重新发送。

但是如果是提交订单或者发起支付的话,就需要更复杂的用户交互和处理方法了。

比如将订单消息发送到消息队列后就立即返回。

这个时候可以在用户端app展示一个进进度条提示,用户订单正在处理中,等待消费者程序完成订单处理后,欢送消息给用户app显示最终的订单处理结果。

异步架构中最主要的技术就是消息队列。

目前主要的消息队列产品有哪些?我有什么优缺点?欢迎你在评论区说说你对消息队列产品的了解,我会和你一起交流,也欢迎把这篇文章分享给你的朋友,或者同事一起交流一下。