Skip to content

消息驱动的架构风格

About 3164 wordsAbout 11 min

架构新书

2026-06-1

定义

​ 消息驱动架构经常被用在构建高可用,高性能,可伸缩性系统。这种架构由消息生产者,消息中间件,和消息消费者构成。消息驱驱动架构适合单体系统,也适合分布式系统。

​ 消息驱动架构的松耦合特性,使得分布式系统之间的混乱调用变成简单清晰架构,可观测。此架构常用于系统之间集成

常见实例

技术案例架构: 基于事件驱动的UI框架,日志框架,数据流处理,工作流引擎,电商秒杀系统,物联网与其他系统集成,企业各系统集成ESB,电商各系统集成,分布式系统常用的的削峰器和负载均衡器。

分布式系统交互,类似单体组件之间的的交互,通常有俩种方式,一种是基于请求-响应风格,系统A发送请求到系统B,系统B处理后返回结果到系统A,在系统B处理完毕结果前,系统A需要等待系统B端响应结果,如用电商订单页面显示运费,会调用运费计算服务,此服务需要基于请求-响应风格,获取运费配置服务获取运费计算规则,后续计算运费。

​ 另外一种方式是异步调用风格,类似UI框架那样,触发UI和响应UI的操作完全解耦。系统A发送请求到系统B后不关心响应,接着完成后续的业务。 系统B通常会发送ACK到系统A,表示收到请求。系统A只关心服务端是否收到,不关心处理结果。系统B响应ACK,再处理具体业务逻辑。

​ 异步调用方式,应用在分布式系统有如下质量问题:

  • 如果系统B不可用,异步调用会失败,期望整个系统有容错性,保证高可用。比如把这些事件路由到其他可用的B实例节点,或者其他系统。
  • 如果系统B出现性能问题,及时采用异步调用方式,即会产生背压现象影响系统A自身性能
  • 如果系统B不可用,希望暂存这些事件,等到系统B恢复后再调用,提供容错性
  • 如果系统A产生大量调用,系统B不能及时处理。也可能导致这些调用会被系统B抛弃,我们希望能控制流量让系统B按照自己的能力处理调用,提供可用性。
  • 希望记录这些调用以方便管理,历史查询,回溯。提高系统可观测性。

背压是指下游处理速度跟不上游的调用量,会进一步影响上游的系统性能。关于背压,在第三章性能优化战术中讲解

回溯是系统的调用被存储起来,已经发生过的一系列调用,被再重新读取执行一次。

提高异步调用的质量特性是采用消息驱动架构风格,高层架构如下,包含了Producer,Consumer和消息中间件

组件描述
Producer调用者,把调用封装为Message,发送个消息中间件
Consumer消费者,从消息中间件获取到Messsage,并做具体业务处理
消息中间件用于存储,转发消息到Consumer,并支持整个系统的高可用,伸缩性,容灾和高性能等质量特性。消息中间又被称为消息平台或者Message Broker。本文统一使用消息中间件作为术语

较为出名的消息中间件 ,有Apache Kafka ,Rabbit MQ,Apache Pulsar,RocketMQ,JMS, ESB系列的产品,Redis的Pub/Sub功能,单体系统EventBus,UI的事件驱动等,使用消息中间件将解决上诉异步调用的问题,有如下优势

  • 解耦系统,当Consumer需要维护的时候,消息中间件可以缓存消息,直到Consumer上线,提高了系统的可用性。
  • 流量调节,大量的Producer请求,通过消息中间件可以暂存,并按照一定流量转发到Consumer。消息中间件管理员可以对消息进行管理,比如物联网设备大量重复上报的属性,导致消息中间件消息积压,可以通过消息中间件删除,只转发最新的消息转发到Consumer。
  • 消息中间件可配置路由功能,比如只有特定的Consumer能消费消息,一个消息可以路由给多种Consumer,比如订单消息可以路由给计费系统,也可以路由给积分系统。
  • 消息中间仅可以配置消息格式转化,比如从JSON方式转化成XML方式,或者把消息进一步封装转发等
  • 负载均衡,当部署多个Consumer实例,消息中间件确保消息只有一个Consumer实例消费,增加Consumer可以提高系统的伸缩性。

消息中间件内部通常使用流水线架构实现 ,其内部架构如下:

  • Channel,用于连接俩个系统,相当于当地的邮局,你信件投递给邮局而不需要关心怎么送达。邮局会转发给信件目的地,或者投送到其他邮局转发。Channel通常都有一个特定的名字,Producer将消息发送到从此名字的Channel。Consumer也从此Channel消费此消息。
  • Router,用于路由消息到一个或者多个Channel。比如对于过时消息将路由到一个专门处理过时消息的Channel,再比如根据Message内容解析后按照表达式结果路由到特定Channel。
  • Translator,转化消息到其他格式,比如为内容为XML的消息增加一个时间戳的字段,或者把XML内容转化为JSON内容
  • 消息网关,如果考虑到可能更换消息中间件,可以提供一个消息网关(以微服务)方式或者封装库。系统调用消息网关而替代直接调用消息中间件的私有API。

ESB和消息中间件区别:面向服务架构风格经常使用ESB,它也是消息中间件,额外增加了更多接入方式以连接各个企业系统而不局限于消息中间件私有API,比如允许多种接入协议不限于SOAP,REST等,也允许已有的开源或者商业CRM,财务系统等产品容易的接入到ESB。 另外ESB也提供了丰富了管理功能,如通道的配置,消息的管理,消息历史查询,以及消息中间件监控等企业特性。关于ESB,将在面向服务架构里说明

消息中间件连接生产者和消费者,通常有2种交互方式

点对点, 消息发送到消息中间件后,只有一个Consumer能收到消息。 如下增值业务系统在给用户提供服务后,将计费事件发送到消息中间件,计费系统将获取此事件进行计费计算,计费原始数据可以提给计费查询计费数据产生的明细进行计费计算。 为了计费服务的高可用,部署多个计费服务,只有一个计费服务能消费计费事件。

广播, 消息发送到消息中间件后,所有订阅此类消息的consumer都能收到。在物联网中,设备上线通过消息中间件发送后,所有订阅方都能收到消息,比如大数据平台可以统计设备上线,设备中心可以保证设备状态为上线状态,全屋系统根据设备上线消息触发全屋计算逻辑。

通常还有其他变化方式

协调者 :协调者既是消费者,也是发布者,比如在工作流应用中,流程引擎接收任务完成事件,根据流程配置,通知相应的节点开始新的任务。这里的流程引擎可部署多个节点以保证高可用和性能

请求-响应 通过创建俩个channel,一个用于请求,一个用于响应。

请求响应模式中,还有一种变化是消息生产者在发送消息前,会请求消息中间件生成一个临时的Channel,并一同发送给消费者。消费者处理完毕后,将响应发送到这个临时channel。 消息生产者通过订阅这个临时channel获得响应。

消息中间件,广泛用于微服系统、企业系统集成。在大规模流量的分布式系统使用消息中间件,可能有如下问题

问题描述
增加链路架构中增加了新的消息中间件,增加了新的调用链路 。消息中间件通常都有成熟可靠的服务,然而,消息中间件仍然可能会发生故障或者性能瓶颈。随着系统的规模扩大,消息中间件也不得不增加部署,其自身的质量属性也需要关注
调试和跟踪消息中间件会选择一个Consumer转发消息,使得消息难以跟踪,必须使用全链路跟踪实现可观测性
大报文传递消息中间件传递大报文会导致消息中间件性能急剧下降并影响其他消息业务。建议大报文通过共享分布式文件系统存储,消息中间件仅转发报文的地址
时序难以保证基于消息的架构不应该假设消息是按照顺序处理,因为顺序处理的性能代价非常大。因为多个Consumer能收到消息,Consumer位于不同的JVM,使得消息处理丢失顺序。及时只有唯一Consumer能收到消息,也因为Consumer内部的并发处理机制导致未按照顺序处理
重复消费消息息中间件不会产生重复的消息,但有俩种情况下Consumer会可能受到重复消息。一个是Producer或者其上游系统端产生了重复消息,另外一种是Consumer收到消息并处理后未及时给消息中间件会确认消息,消息中间件可能会重发此消息。需要Consumer具备去重功能
消息积压对于系统之间直连调用,B系统可以采用快速失败方式提高系统A和系统B的高可用。.但在消息驱动的架构里,消息中间件会暂存消息导致消息积压,系统出现了大量无用过时消息。 建议Consumer在收到信息后,最先做的就是业务处理是判断消息是否过时,否则,让Consumer一直处理过期消息毫无意义。有的消息中间件支持自动过时清理,或者把这种消息发到专门的过时消息通道

注意:上诉时序问题,重复消费消息,以及消息中间件积压消息的解决战术将在第三章高可用战术中详细说明

云厂商的消息中间件和ESB产品提供了丰富消息处理功能,架构师需要谨慎对待这些现成功能,权衡是采用这些功能以体现集成特性,还是作为一个业务需求直接通过业务代码实现来体现业务特性

比如消息中间件消息积压问题,既可以使用消息中间件消息过时处理机制,也可以编写Consumer代码去忽略过时消息以快速处理。再比如,消息中间件支持按照业务规则将一条消息拆分成若干消息后放入不同的通道给不同的消费者, 也可以通过提Consumer手动编写代码实现路由分发。在大型企业的SOA架构里,前者更合适,体现了大型系统之间的集成特性,如果是一个微服务系统,或者只拥有一个功能简单消息中间件,编写代码更合适,体现了业务特性

知行合一