消息队列是在消息传输过程中保存消息的容器,消息队列管理器在将消息从源到目标时充当中间人的角色,消息队列的主要目的是提供路由并保证消息的可靠传递。如果发送消息时接收者不可用,那消息队列就会保留消息,直到下次成功消费为止。
分布式消息系统通过提供消息传递和消息排队模型,在分布式环境下扩展进程间的通信,并支持多通信协议、语言、应用程序、软硬件平台,实现系统之间的可靠异步消息通信,并保障数据再复杂网络中的高效传输、稳定、安全、可靠,以及分布式环境下的高可用性和一致性,常用在应用解耦、异步通信、流量削峰填谷、日志收集、缓存更新、数据同步、事务最终一致性等典型场景。
在云原生时代,微服务架构的大规模应用对链路间的可靠性传输提出了更大的挑战,而消息服务作为应用通信的基础设施,逐渐成为微服务架构应用的核心依赖,同时也是实践云原生核心设计理念的关键技术。架构师和核心开发人员通过分布式消息系统可以搭建出分布式、高性能、弹性、稳定的应用程序。
消息服务在云原生架构中的重要性也导致其极有可能成为应用实践云原生的阻塞点,所以消息服务的云原生化也是至关重要的。在云原生时代,消息队列不仅解耦不同的微服务,而且随着 Serverless、Service Mesh 等技术的广泛应用,分布式消息队列的使用场景也得到了进一步的扩展从可靠异步传输链路的基础设施,逐渐演变为事件传递的中枢神经,连接着各种各样的云服务、云应用和 SaaS 产品,提升了产品的集成与被集成能力,消息形态也从单纯的消息裸数据逐渐转变为更高维度的事件抽象。
2、典型技术和架构
当前主流的主要有 Apache Kafka、RabbitMQ、ActiveMQ、RocketMQ 等。
- RabbitMQ:开源的消息队列中间件,支持多种消息协议和消息模式,适用于广泛的应用场
- ActiveMQ:基于 JMS 规范的消息队列实现,提供了丰富的特性和可靠性保障
- Apache Kafka:由 LinkedIn 开发的高吞吐量分布式消息系统,旨在为处理实时数据提供一个统一、高通量、低延迟的平台。最大特性是可以实时处理大数据以满足各种需求场景
- RocketMQ:一款低延迟、高可靠、可伸缩、易于使用的消息中间件,由阿里巴巴贡献给 Apache,采用发布-订阅模式传递消息,具有高效灵活的水平扩展能力和海量消息堆积能力
消息队列一般都采用如下图所示的中心化架构:
(图片来自互联网)
分布式消息队列包含以下模块:
- 客户端:提供了消息的接收和订阅 API,同时支持重试、熔断等高可用功能
- 注册中心:提供了集群管理、元数据管理、路由和服务发现等功能
- 计算节点:在消息队列的服务端 Broker 中,计算部分包含高性能的传输层以及可扩展的 RPC 框架,用于处理来自客户端的不同请求
- 存储引擎:Broker 的核心是存储引擎,某些消息队列可能会将存储引擎与计算节点拆分开来,主要是为了消息提供高性能持久化,以队列方式组织消息,用以保证消息必达
3、云原生时代的挑战
在云原生时代,虽然消息队列已经成为了云原生架构的底层通信基础设施,但是随着使用场景的逐步扩展,也面临着更大的挑战,主要来自以下几个方面的挑战:
高 SLA
消息队列在应用系统中一般用于业务的核心链路,因此业务系统对消息队列的可用性要求极高,在云原生时代,云原生应用对消息这种云原生 BaaS 有更高的 SLA 要求,应用将假设它依赖的云原生服务具备和云一样的可用性,从而无须建设备份链路来提高应用的可用性,降低了架构的复杂度。
高性能
随着越来越多的行业场景开始使用消息队列,就随之而来更高的要求。除了容量之外,更低的写入延迟,更短的端到端延时,更平稳的性能曲线都是需要关注的重点。
极致弹性
消息队列是有状态服务,云原生时代的消息队列最大的改进在于从用户的视角彻底消除了状态,在逻辑资源和物理资源两个维度真正做到了按需使用。消息队列与 Serverless 的结合正好将这种需求做到了极致。架构师和研发者更关心消息实例提供的逻辑资源是否充足,队列数量是否能满足扩展性的需求。在服务提供者侧,我们更关心的是如何降低运维的成本,系统可以根据集群 Load 等指标自动扩容或收缩 MQ 物理资源,而 Kubernetes 和 Serverless 等技术的大量使用,使得这种极致的弹性扩缩能力成为可能。
标准
为避免厂商锁定,同时提升多种消息队列的互通性,云原生时代的消息队列必定是遵循开源和开放标准的。在消息领域,无论是接口还是协议,社区一直有很多事实标准用于提高消息队列的易用性,比如,Kafka 提供的 API 和协议、JMS API、CloudEvents 规范,MQTT 中的协议和模型,AMQP 的协议和模型等。以阿里云为代表的云平台的消息队列产品对这些事实标准都提供了相应的接入方式,企业可以低成本地完成迁移上云。然而,事实标准如果太多,其实就是没有标准。OpenMessaging 作为云原生时代的分布式消息队列标准,得到了越来越多云平台的支持,它将提供六大核心特性:多领域、流、平台无关、标准的 Benchmark、面向云和线路层可插拔。
原生事件支持
事件相对于消息更加具象化,代表了事情的发送、条件和状态的变化。事件源来自不同的组织和环境,所以事件总线天然需要跨组织。事件源对事件将如何响应没有任何预期,所以采用事件的应用架构是更彻底的解耦,将具备更好的可扩展性和灵活性。云原生时代的消息队列已经不只停留在消息层面,而是对更高维度的事件提供支持,这不仅消除了微服务之间的耦合,甚至在企业的组织架构层面都做了解耦,提升了应用本身的集成与被集成能力。同时,消息队列对事件的更好支持,也使得 EDA(Event-Driven Architecture,事件驱动架构)在微服务领域得到广泛的应用。
4、RocketMQ 阿里实践案例分析
阿里巴巴内部业务众多,微服务化带来了大量的系统间交互。其中,消息是不同微服务间进行交互所不可缺少的一环,尤其是对于交易、物流等核心业务链路而言。阿里巴巴内部的使用场景具体包括异步解耦、削峰填谷、数据同步、事务最终一致性保证等。
下面以电商业务为案例来讲述消息系统的具体使用。
在电商业务中,用户的下单流程可以简单地分为两个阶段,支付阶段和物流阶段,这两个阶段的业务逻辑是由两个相互独立的微服务完成的,如下图所示,下单服务编排了 Alipay 和菜鸟两个服务,以完成整个网购流程。
异步解耦
异步解耦本质上是一个同步转异步的过程,在系统间的交互过程中,最大程度避免上游服务对下游服务的依赖,并保证整个业务链路的完整性与正确性。
在上述网购业务中,相较于支付服务,物流服务中包含的业务链路更长。所以,在用户购买服务中,如果需要等待物流服务返回具体的结果,则不仅会影响用户体验,而且会极大降低系统性能。因此,阿里巴巴内部使用分布式消息队列 RocketMQ 来解耦上游支付系统与物流订单系统的耦合。
通过异步解耦,系统只需要在支付完成、发送物流消息成功后,向用户返回购买成功的信息即可,而不用等待物流系统实际处理后再返回。这不仅提升了用户体验,还提升了系统容量,同时降低了系统开发的复杂度。
削峰填谷
随着电商业务的蓬勃发展,大促已经成为电商领域非常常见的营销模式,但大促会对系统带来超过平时数倍乃至上百倍的数据压力,因此需要引入 RocketMQ 分布式消息队列。RocketMQ 的引入,不仅可以解决支付系统与物流系统的耦合,而且在大促流量达到洪峰后,还可以通过自身所具有的亿级消息堆积能力,极大程度地降低物流服务被冲垮的风险,使得物流服务可以按照自身的实际处理能力对业务进行处理。同时,由于 RocketMQ 提供了消息必达的能力,能够避免消息丢失给用户带来损失,保证了整个业务处理的完整性与正确性。
事务最终一致性
回到上面的案例,虽然我们已经把整个电商网购业务简单地拆分成两个阶段,但是仍不可避免地要对系统之间可能产生的异常进行容错处理。那么,我们就要面临一个非常简单的问题,到底是应该先扣款支付,还是应该先发物流消息。如果先发物流消息,而扣款不成功,则将给平台带来极大的损失;而一旦选择先扣款,如果扣款成功,消息发送却失败,则将会发生用户付款了却收不到货的情况,这将给用户造成损失;因此,这两种选择都是不可接受的。
在这个案例中,我们使用 RocketMQ 提供的事务消息轻松解决了这个问题。在事务消息的解决方案中,我们首先会发送一条 prepare 消息到 RocketMQ 集群,此时,该消息并不会被下游物流服务收到;发送 prepare 消息成功后,我们会进行实际的扣款,如果扣款成功,就会提交上述 prepare 消息,并投递到下游物流服务中,而如果扣款失败,我们就会回滚上述 prepare 消息,避免下游物流服务收到该消息。此时,我们需要面临另外一个问题:如果在提交或回滚的过程中,当前处理业务的下单服务实例宕机怎么办?RocketMQ 通过回查机制解决了这个问题,如果一条prepare消息在很长时间后还没有提交或回滚,那么 RocketMQ 就会主动向集群中的其他下单服务发起回查,其他实例会根据实际扣款结果来决定是否提交或回滚该 prepare 消息。以 RocketMQ 为代表的分布式消息队列通过这种方式解决了系统开发过程中常见的分布式事务问题。
分布式消息队列技术是云原生架构设计中的重要组成部分,为构建高效、可靠的云原生应用系统提供了关键支持。在未来,随着云原生架构的不断演进,分布式消息队列技术将扮演者越来越重要的角色,推动者云原生应用的发展和创新。