本文转自腾讯云中间件,作者张超,腾讯数据平台部 MQ 团队高级工程师,Apache TubeMQ(incubating) PMC,Kafka-on-Pulsar Maintainer,Apache Pulsar Contributor
腾讯数据平台数平 MQ 团队对 Pulsar 做了深入调研以及大量的性能和稳定性方面优化,目前已经在腾讯云消息队列 TDMQ 落地上线。本文主要简单梳理了 Pulsar 支持的一些传统消息队列应用场景,以及 Pulsar 新特性对更多场景的支持。关于 Apache Pulsar
Apache Pulsar 是 Apache 软件基金会*项目,是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体,采用计算与存储分离架构设计,支持多租户、持久化存储、多机房跨区域数据复制,具有强一致性、高吞吐、低延时及高可扩展性等流数据存储特性。
GitHub 地址:http://github.com/apache/pulsar/
消息队列概述
什么是消息队列
消息队列(Message Queue,简称MQ),是指在消息的传输中保存消息的容器或服务,是一种异步的服务间通信方式,适用于无服务器和微服务架构,是分布式系统实现高性能、高可用、可伸缩等高级特效的重要组件。
常见的主流消息队列有 ActiveMQ、RabbitMQ、ZeroMQ、Kafka、MetaMQ、RocketMQ、Pulsar 等。而在公司内有 TubeMQ、Ckafka、TDMQ、CMQ、CDMQ、Hippo 等。
消息队列特点
分布式
消息队列都是分布式的,因此才可以提供异步、解耦等功能。
可靠性
基于消息的通信是可靠的,消息不会丢失。大多数消息队列都提供将消息持久化到磁盘的功能。
异步
通过消息队列,可将远程同步调用拆解成为异步调用。对于不需要获取远程调用结果的应用场景来说,性能提升明显。
松耦合
消息直接由中间件存储和分发。消息生产者只需关注如何将消息发送给消息中介服务器;消费者只需关注如何从中介服务器订阅。生产者和消费者之间是完全解耦的,不需要知道彼此的存在。
事件驱动
可以将复杂的应用系统重构成为事件驱动的系统。事件溯源(Event Sourcing),表示一个对象从创建到消亡,会经过的多种状态。如果把对象的状态变化都存储下来,不但可以根据状态变化记录获取对象的当前状态,也可以回溯对象的变化过程。消息队列能很好地支持这样的系统设计方式,将触发对象状态变化的事件放入消息队列。
消息队列分类
在 JMS(JAVA Message Service)标准中,有P2P(Point to Point)和 Publish/Subscribe(Pub/Sub) 两种消息模型。
P2P
P2P的特点是每个消息只有一个消费者。消息生产者将消息发送到消息队列(Queue)中,只有一个消费者能够消费此消息,消费完成之后消息即删除。任意一个消费者都可以消费这个消息,但消息绝对不会被两个消费者重复消费。
Pub/Sub
Pub/Sub 的特点是发布到 Topic 的消息会被所有订阅者消费。消息生产者将消息发送到消息主题(Topic)中,所有订阅这个主题的消费者都可以消费此消息,当所有订阅者都消费完成之后才能删除消息。
消息的生产者和消费者之间有时间依赖,只有事先订阅这个主题的消费者才可消费。如果先发送消息,后订阅主题,那么订阅之前的消息将不能被这个订阅者消费。
传统企业型消息队列 ActiveMQ 遵循了 JMS 规范,实现了点对点和发布订阅模型,但其他流行的消息队列 RabbitMQ、Kafka 并没有遵循 JMS 规范。
而在实时流式架构中,消息队列的消息传递可以分为队列(Queue)和流(Stream)两类。
队列(Queue)模型
队列模型主要是采用无序或者共享的方式来消费消息。通过队列模型,用户可以创建多个消费者从单个管道中接收消息;当一条消息从队列发送出来后,多个消费者中的只有一个(任何一个都有可能)接收和消费这条消息。消息系统的具体实现决定了最终哪个消费者实际接收到消息。
队列模型通常与无状态应用程序一起结合使用。无状态应用程序不关心排序,但它们确实需要能够确认(ACK)或删除单条消息,以及尽可能地扩展消费并行性的能力。典型的基于队列模型的消息系统包括 RabbitMQ 和 RocketMQ。
流式(Stream)模型
相比之下,流模型要求消息的消费严格排序或独占消息消费。对于一个管道,使用流式模型,始终只会有一个消费者使用和消费消息。消费者按照消息写入管道的确切顺序接收从管道发送的消息。
流模型通常与有状态应用程序相关联。有状态的应用程序更加关注消息的顺序及其状态。消息的消费顺序决定了有状态应用程序的状态。消息的顺序将影响应用程序处理逻辑的正确性。典型的基于流模型的消息系统包括 Kafka、TubeMQ。
传统消息队列的应用场景
异步调用
假设有一个系统调用链路为 A 调用 B 耗时 20ms,B 调用 C 耗时 20ms,而 C 调用 D 需要 2s,这样下来整个调用需要耗时 2040ms。但实际上 A 调用 B,B 调用 C 只需要 40ms,而 D 系统的引入直接导致系统性能下降约 50 倍。此时我们可以考虑引入消息队列,将 D 系统的调用抽离出来,做一个异步调用:系统 A 到系统 B 再到系统 C 后就直接结束,系统 C 将消息发送到消息队列中,系统 D 从消息队列里取消息进行消费,这样子我们系统的性能就提高了接近 50 倍。
系统解耦
各个业务系统仅需要处理自己的业务逻辑,发送事件消息到消息队列。下游业务系统直接订阅消息队列的队列或主题获取事件。消息队列可用于单体应用被拆解为微服务后不同微服务间的通信。系统解耦的好处是不同系统的迭代不再相互依赖,能有效缩短数据链路长度,提高数据处理效率。
削峰填谷
大型活动带来较高流量时,没有做好相应保护容易导致系统超负荷甚至崩溃,而限制太过则会导致请求大量失败而影响用户体验。消息队列服务拥有高性能的消息处理能力,可以承接流量脉冲而不被击垮,在确保系统可用性的同时,通过快速有效的请求响应技术提升用户体验。其海量消息堆积能力确保下游业务在安全水位内平滑稳定的运行,避免流量高峰的冲击。
广播通知
系统一个状态的改变,需要通知多个相关系统,可通过消息订阅的方式推送给各个订阅者系统。比如数据库值的改变,需要通知所有的缓存系统更新,可以把数据库值改变发送消息给消息队列,然后各缓存订阅相关主题,收到消息后更新自己的缓存。
分布式缓存
在大数据场景中,日志分析往往需要处理大量日志,不可能存储在一台物理机上。消息队列可提供一个集群,用来存储海量消息,将其缓存到消息队列,进一步供实时分析系统分析日志。Kafka 和 TubeMQ 在大数据处理中往往充当分布式缓存的作用。
消息通讯
消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
Pulsar的应用场景
Pulsar 作为新一代存储计算分离架构的消息队列服务,不仅适用于上面提到的传统消息队列的应用场景,它的一些新特性还为更多应用场景带来可能。
队列和流的融合—维护一套 MQ 服务就够了
Apache Pulsar 抽象出了统一的 producer-topic-subscription-consumer 消费模型,既支持队列模型,也支持流模型。在 Pulsar 的消息消费模型中,Topic 是用于发送消息的通道。每一个 Topic 对应着 Apache BookKeeper 中的一个分布式日志。发布者发布的每条消息只在 Topic 中存储一次;存储的过程中,BookKeeper 会将消息复制存储在多个存储节点上;Topic 中的每条消息,可以根据消费者的订阅需求,多次被使用,每个订阅对应一个消费者组。尽管消息仅在主题(Topic)上存储一次,但是用户可以有不同的订阅方式来消费这些消息:
- 消费者被组合在一起以消费消息,每个消费组是一个订阅。
- 每个 Topic 可以有不同的消费组。
- 每组消费者都是对主题的一个订阅。
- 每组消费者可以拥有自己不同的消费方式:独占(Exclusive),故障切换(Failover)或共享(Share)。
Pulsar 通过这种模型,将队列模型和流模型这两种模型结合在了一起,提供了统一的 API 接口。这种模型,既不会影响消息系统的性能,也不会带来额外的开销,同时还为用户提供了更多灵活性,方便用户程序以最匹配模式来使用消息系统。
多种 MQ 协议兼容—轻松迁移传统 MQ 服务
在 Pulsar 架构中,为了处理 Bookie 存储消息和防止消息丢失等,基于 Managed Leger 实现了一套分布式的流程封装。Pulsar Protocol Handler 处理 Pulsar 中生产者和消费者发送出来的 TCP 请求,将其转化为可读取状态的操作。Pulsar 2.5 版本后,将 Protocol Handler 接口单独脱离了出来,利用这个框架就可以单独实现自定义协议的转换,比如 Kafka、AMQP 等,可以帮助存量的 MQ 业务轻松迁移到 Pulsar。
Kafka Protocol Handler 目前是一个独立的项目在维护— Kafka On Pulsar(简称KoP),由于公司内存量 Kafka 业务很多,数平 MQ 团队针对 KoP 做了大量的优化工作,腾讯现在有其他团队也在更深度参与KoP项目,详情可以参考腾讯加盟:Kafka-on-Pulsar 项目迎来 2 位腾讯 Maintainer!
企业级多租户特性—数据安全有保证
作为企业的消息中枢,Apache Pulsar 自诞生之日起就支持多租户,因为该项目最初就是为了满足 Yahoo 的严格需求,而当时市面上没有任何可用的开源系统能够提供多租户功能。在 Pulsar 的设计中,租户可以跨集群分布,每个租户都可以有单独的认证和授权机制;租户也是存储配额、消息 TTL 和隔离策略的管理单元。Pulsar 通过下列方式满足了多租户场景下的数据安全:
- 通过为每个租户进行身份验证、授权和 ACL(访问控制列表)获得所需安全性。
- 为每个租户强制实施存储配额。
- 以策略的方式定义所有隔离机制,策略可在运行过程中更改,借此降低运维成本并简化管理工作。
跨地域复制—自带跨机房冗灾能力
在大型的分布式系统中,都会涉及到跨多个数据中心的需求。在对服务质量和灾备要求更高的场景中,会规划将机房部署在地理位置分散的多个数据中心内。在此类多数据中心部署中,通常会使用跨地域复制机制提供额外的冗余,以防某个数据中心故障、自然侵害或其他事件导致服务无法正常运作。Apache Pulsar 在设计之初就加入了对 Yahoo 全球十多个机房的跨地域复制的需求。Apache Pulsar 的跨地域多机房互备特性是 Pulsar 企业级特性的重要组成部分,它在保证数据稳定可靠的同时,为用户提供了便捷的操作和管理。
在上图中,无论 Producer P1、P2 和 P3 在什么时候分别将消息发布给 Cluster A、Cluster B 和 Cluster C 中的 Topic T1,这些消息均会立刻复制到整个集群。一旦完成复制,Consumer C1 和 C2 即可从自己所在的集群消费这些消息。
Pulsar 的跨地域复制不仅应用在跨数据中心数据备份的场景,在 PowerFL 联邦学习平台中跨地域复制的能力还被用来做通信服务使用。
云原生支持—助力服务上云
云原生的原生即软件设计之初就考虑到了将来会被运行在云端的可能,从而在设计层面上就充分利用了云资源的特点,典型的是分布式和弹性伸缩的能力。Pulsar 之所以说是云原生的消息平台,核心就是它的架构设计能够充分利用分布式的、能够弹性伸缩的云端资源。以 Pulsar on Kubernetes 为例,Bookie 是有状态的节点,但是节点之间是对等的,可以采用 StatefulSet 来部署;而 Broker 作为无状态的节点,直接使用 ReplicaSet 即可,每个 Pod 支持水平扩展。
目前公司已经有业务使用 Pulsar on Kubernetes,如果 bookie 使用 Local Storage Volume,对 Pulsar 的性能基本没有影响。