8.1.2 使用消息传递在服务之间传达状态更改

使用消息传递方式将会在许可证服务和组织服务之间注入队列。该队列不会用于从组织服务中读取数据,而是由组织服务用于在组织服务管理的组织数据内发生状态更改时发布消息。图8-2演示了这种方法。
 
 
图8-2 当组织状态更改时,消息将被写入位于两个服务之间的消息队列之中
    
在图8-2所示的模型中,每次组织数据发生变化,组织服务都发布一条消息到队列中。许可证服务正在监视消息队列,并在消息进入时将相应的组织记录从Redis缓存中清除。当涉及传达状态时,消息队列充当许可证服务和组织服务之间的中介。这种方法提供了以下4个好处:
    松耦合;
    耐久性;
    可伸缩性;
    灵活性。
    1.松耦合
    微服务应用程序可以由数十个小型的分布式服务组成,这些服务彼此交互,并对彼此管理的数据感兴趣。正如在前面提到的同步设计中所看到的,同步HTTP响应在许可证服务和组织服务之间产生一个强依赖关系。尽管我们不能完全消除这些依赖关系,但是通过仅公开直接管理服务所拥有的数据的端点,我们可以尝试最小化依赖关系。消息传递的方法允许开发人员解耦两个服务,因为在涉及传达状态更改时,两个服务都不知道彼此。当组织服务需要发布状态更改时,它会将消息写入队列,而许可证服务只知道它得到一条消息,却不知道谁发布了这条消息。
    2.耐久性
    队列的存在让开发人员可以保证,即使服务的消费者已经关闭,也可以发送消息。即使许可证服务不可用,组织服务也可以继续发布消息。消息将存储在队列中,并将一直保存到许可证服务可用。另一方面,通过将缓存和队列方法结合在一起,如果组织服务关闭,许可证服务可以优雅地降级,因为至少有部分组织数据将位于其缓存中。有时候,旧数据比没有数据好。
    3.可伸缩性
    因为消息存储在队列中,所以消息发送者不必等待来自消息消费者的响应,它们可以继续工作。同样地,如果一个消息消费者没有足够的能力处理从消息队列中读取的消息,那么启动更多消息消费者,并让它们处理从队列中读取的消息则是一项非常简单的任务。这种可伸缩性方法适用于微服务模型,因为我通过本书强调的其中一件事情就是,启动微服务的新实例应该是很简单的,让这些追加的微服务处理持有消息的消息队列亦是如此。这就是水平伸缩的一个示例。从队列中读取消息的传统伸缩机制涉及增加消息消费者可以同时处理的线程数。遗憾的是,这种方法最终会受消息消费者可用的CPU数量的限制。微服务模型则没有这样的限制,因为它是通过增加托管消费消息的服务的机器数量来进行扩大的。
    4.灵活性
    消息的发送者不知道谁将会消费它。这意味着开发人员可以轻松添加新的消息消费者(和新功能),而不影响原始发送服务。这是一个非常强大的概念,因为可以在不必触及现有服务的情况下,将新功能添加到应用程序。新的代码可以监听正在发布的事件,并相应地对它们做出反应。
    8.1.3 消息传递架构的缺点
    与任何架构模型一样,基于消息传递的架构也有折中。基于消息传递的架构可能是复杂的,需要开发团队密切关注一些关键的事情,包括:
    消息处理语义;
    消息可见性;
    消息编排。
    1.消息处理语义
 
在基于微服务的应用程序中使用消息,需要的不只是了解如何发布和消费消息。它要求开发人员了解应用程序消费有序消息时的行为是什么,以及如果消息没有按顺序处理会发生什么情况。例如,如果严格要求来自单个客户的所有订单都必须按照接收的顺序进行处理,那么开发人员必须有区别地建立和构造消息处理方式,而不是每条消息都可以被独立地使用。
    这还意味着,如果开发人员正在使用消息传递来执行数据的严格状态转换,那么就需要在设计应用程序时考虑到消息抛出异常或者错误按无序方式处理的场景。如果消息失败,是重试处理错误,还是就这么让它失败?如果其中一个客户消息失败,那么如何处理与该客户有关的未来消息?这些都是需要考虑的问题。
    2.消息可见性
    在微服务中使用消息,通常意味着同步服务调用与异步处理服务的混合。消息的异步性意味着消息在发布或消费时,它们可能不会被立刻接收或处理。此外,像关联ID这些在Web服务调用和消息之间用于跟踪用户事务的信息,对于理解和调试应用程序中发生的事情是至关重要的。读者可能还记得在第6章中,关联ID是在用户事务开始时生成的唯一编号,并与每个服务调用一起传递,此外,它还应该在每条消息被发布和消费时被传递。
    3.消息编排
 
正如在消息可见性的那部分中提到的,基于消息传递的应用程序更难按照应用程序的执行顺序进行业务逻辑推理,因为它们的代码不再以简单的块请求-响应模型的线性方式进行处理。相反,调试基于消息的应用程序可能涉及多个不同服务的日志,在这些服务中,用户事务可以在不同的时间不按顺序执行。
    
消息传递可能很复杂但很强大
    前面几小节并不是为了吓跑大家,让大家远离在应用程序中使用消息传递。相反,我的目的是强调在服务中使用消息传递需要深谋远虑。我最近完成了一个主要的项目,需要为每个客户开启和关闭有状态的AWS服务器实例集。我们必须使用AWS简单排队服务(Simple Queuing Service,SQS)和Kafka来集成微服务调用和消息的组合。虽然这个项目很复杂,但是在项目结束时,我亲眼看到了消息传递的强大功能。我们的团队意识到我们需要处理的问题是,在服务器被终止之前,我们必须确保从服务器上提取某些文件。这一步骤占据大约75%的用户工作流程,并且整个流程只有在这一步完成之后才能继续进行。幸运的是,我们有一个微服务
(称为文件恢复服务),它会检查正在退出的服务器是否已将文件提取出来。由于服务器通过事件传递了所有的状态变化(包括它们正在退出),所以我们只需要将文件恢复服务器插入来自正在退出的服务器的事件流中,并让它们监听“olecommissioning”事件。
    如果整个过程都是同步的,那么增加这个文件排查的步骤将是非常痛苦的。但是在最后,我们只需要一个在生产中已存在的现有服务,来监听来自现有消息队列的事件并作出反应。这项工作是在几天内完成的,我们在项目交付过程中从没出过任何差错。通过消息,开发人员可以将服务挂钩在一起,而不需要将服务在基于代码的工作流中硬编码到一起。
 
 
posted @ 2019-12-03 10:54  mongotea  阅读(203)  评论(0编辑  收藏  举报