42 消息中台:如何搭建企业内部统一的消息服务?
你好,我是文强。
在 第03讲 讲协议的时候,我们知道了协议分为公有协议和私有协议。公有协议有MQTT、AMQP、OpenMessaging等等,主流消息队列一般会设计自己的私有协议,比如Kafka 协议、Pulsar 协议、RocketMQ 的 Remoting 协议等。另外消息队列从使用场景的角度看,分为了消息和流两个方向。所以在业务架构中,一般会同时使用多种消息队列来满足不同的使用场景。
但是同时使用多款消息队列,从业务视角来看会增加学习和使用成本,从公司视角看会增加运维和资源成本。所以在一些有基础架构团队的企业,都会有收拢消息队列服务的需求,因此搭建消息中台总会被提起或者提上日程。
那么这节课我就来讲一下如何搭建企业内部中台型的消息服务,会围绕主要的技术方案展开,详细分析每个方案的实现思路以及优劣势。希望能对你有所帮助!
统一的消息服务
首先我们来看一下什么是统一的消息服务。
它是指收拢企业内部的消息组件,集中交付、运维、管理等等。业务侧不需要过多关心运维运营方面的细节,专注使用就好。
PaaS 化和中台化
从实际落地的角度来看,统一消息服务一般有PaaS 化和中台化两种方案。
- PaaS 化,指由统一的团队提供各种开源的消息队列服务。当业务需要某款消息队列时,基础团队负责交付、运维、故障处理、监控体系搭建等工作,业务侧只需要负责使用即可。
- 中台化,指通过提供统一的接入层负责消息的读写。屏蔽底层细节,以降低业务方使用成本和切换成本,规范业务使用方式,从而提升系统稳定性,加快处理问题的速度。
但某种意义上说,PaaS 化只是降低了一部分业务团队使用消息队列过程中的搭建、维护、故障处理成本,但是各个业务系统还是直接使用的各种开源消息队列,所以还是会遇到各种问题,比如功能、稳定性、容灾等等。
中台化则会提供统一的接入层,来支持各个业务的接入使用。如下图所示,统一接入层的存在对业务屏蔽了底层具体的存储引擎、容灾情况、使用率和运营调度等细节。它的好处是业务只需要关注使用,这样可以极大地降低使用成本,并且可以通过提高资源利用率来降低资源成本,通过多种容灾策略来提升系统稳定性。
那这里就有一个问题,我们应该选择哪种方案呢?
两种方案对比
从技术上看,中台化相对 PaaS 化最大的区别是: 通过提供统一接入层来屏蔽底层消息引擎的细节,从而降低业务侧的理解和使用成本。 即业务不需要关注底层部署运行的是什么消息引擎、是否做好了容灾、如何完成主备/主从切换、是否部署连接器同步数据等等。
从技术上看,中台化有两个明显的好处。
- 通过提供统一的接入层将基础能力下沉,屏蔽底层细节,可以降低业务侧使用消息队列的成本,让业务侧更专注业务。
- 底层的变更无需用户感知,因此可以不断升级优化底层架构,部署容灾策略,甚至切换底层引擎,从而提升消息队列服务的稳定性,保证业务长期稳定地运行。
而中台化最大的缺点是 开发成本极高, 需要投入大量的研发人力去建设平台。它的价值只有在业务规模大、子业务多、业务对稳定性和可用性要求高的企业中才能更好地体现出来。
如果一个企业只有少数消息队列需求,且在使用、运维、升级方面的成本不高的情况下,用 PaaS 化的方案就够了。这也是为什么一般只有一些大企业才会建设自己的中台化消息服务。
所以总结来看,消息中台的必要性是根据企业的规模、使用量、对稳定性的需求来评估的。 是一个可选项,而不是一个必选项。
清楚了什么是统一的消息服务,接下来我们详细聊聊它的两种实现形态,PaaS 化和中台化的技术实现思路。
PaaS化的技术实现分析
消息队列服务 PaaS 化是一种半托管的形态。如下图所示,基础服务团队负责搭建对应的 PaaS 平台,提供各种基础的消息队列服务,并提供对应的配套设施,比如基础运维平台,对接日志、监控、告警平台等等。
从具体落地来看,工作量分为平台搭建和成本/稳定性两部分。
平台搭建 指搭建基础 PaaS 运维平台,需要提供集群自动化部署、升级、扩缩容、集群资源增删改查、监控、告警、日志等能力。
集群部署的自动化主要依赖运维手段来完成,比如公司的CI/CD系统,运维常用的工具Ansable等。监控、告警、日志一般集成公司内部的系统,或者如我们在第21~22讲中提到的方案,ELK、Grafana + Prometheus等。这块的思路相对清晰,执行也有比较多的方案可以参考。
成本/稳定性 指集群容灾部署、提升资源利用率、弹性扩缩容、紧急故障恢复、日常问题处理SOP等工作。这一块挑战是比较大的,只有深入了解对应的消息引擎的底层原理和源码,才能做好这部分的事情。因此如果需要运维多个消息队列,成本就会上升很多。
消息队列服务 PaaS 化,主要的工作量就是上面说的那几点,技术难度不高,这里就不详细展开讲了,有需要的话我们课后讨论。接下来我们重点讲一讲中台化的技术实现思路。
中台化的技术实现分析
先来看一张消息中台的系统架构图。
如上图所示,从技术上拆解消息中台,应该包括底层存储引擎的选择、接入层协议设计、接入层开发、自定义SDK开发、配套基础设施开发五个部分。
接下来我们就分析一下这五个部分的实现,先来看一下存储引擎的选择。
存储引擎的选择
从单个企业的角度看,不管对于大企业还是中长尾企业,自研消息内核的成本都非常高,周期也很长。因此,如果业务上没有开源组件满足不了的痛点,基本不会走这条路。所以底层存储引擎一般会选择业界主流的开源消息队列,比如 Kafka、RocketMQ、Pulsar 等等。
从落地的角度,一般会选择一个当前阶段比较适合的引擎。
RabbitMQ 是业务消息类的消息队列,架构上有一些天然的缺陷,比如一致性协议的设计导致其性能不高,很难满足一些大流量和高并发的消息需求。网络分区的存在导致在一些极端的场景下可能会出现消息服务中断的问题。而 Pulsar 作为一个新兴的消息队列,稳定性和功能完整度还不够承载现网的业务。
所以综合考虑,一般会在Kafka 和 RocketMQ 中做选择。而选型主要考虑以下两个方面:
- 功能和流量方面的需求,即根据公司的业务形态去评估这两方面诉求的侧重点。这点跟消息队列选型的思路是一致的,可以参考一下 第02讲。
- 比较主观,就是公司人员对于这两款消息队列的熟悉程度和把握度。
但是选型并不是二选一。因为接入层的存在,底层是可以同时运行多种引擎来提供服务的,所以在具体引擎的选择上,可以根据业务需求随时切换,不会涉及客户端的修改。这也是消息中台的好处之一。
接下来我们来看看接入层的协议设计。
接入层协议设计
接入层协议设计,跟我们在 第03讲 讲到的协议设计思路是一致的。所以接下来我们主要讨论接入层用什么协议?
从落地角度看,因为私有协议的可控性和灵活度高,所以消息中台接入层的协议一般都是私有协议。私有设计跟正常开源协议的设计思路不太一样,主要考虑以下三点:
- 消息中台的协议是给企业内部使用的,不需要太考虑协议的通用性和兼容性,只要能满足业务的需求即可。
- 根据二八原则,企业内部业务大部分不需复杂的消息队列功能,所以说开源协议中的很大一部分协议对于消息中台是没有意义的。
- 因为是公司内部的服务,业务侧是可以沟通和定义使用规范的,所以可以通过和业务沟通,降低协议设计的复杂度。
因此, 如果是消息中台服务,一般会建议自定义私有消息队列,主要目标是简化协议的设计,降低开发成本以及业务的使用理解成本。
这里其实还有一点,如果企业已经在使用开源的消息队列,此时自定义了私有协议,那这些存量服务该怎么处理呢?
正常的思路就是,要将这些开源集群的服务迁移到消息中台里面,然后通过统一的接入层提供服务。但是这里有两个问题,一个是底层集群的迁移方案会很复杂,另外一点就是业务侧需要进行SDK切换的改造。从工作量来看,这两个问题的解决方案都很复杂,特别是第二点,业务侧的改造时间很难协调。
所以,就得考虑是否在接入层适配多种开源协议?这一点我们放在思考题中探讨。
接下来我们再看一下接入层是如何开发的。
接入层开发
从技术上看,消息中台接入层的开发用的是代理(Proxy)的思路。
如上图所示,接入层主要由 网关(如 Nginx)、 元数据模块、 接入层集群 三部分组成。网关用于流量接入、拦截和分发;元数据模块用来存储接入层相关的元数据信息;接入层集群用于接收流量,然后转发到存储层。
从开发角度看,主要开发工作量是在接入层集群。接入层集群一般需要考虑集群构建、协议解析、元数据管理、集群拆分、连接管理五个部分。
集群构建,指设计实现接入层集群。我们在 第36讲 讲到存算分离架构中的计算层一般是有状态的。而消息中台接入层只需要负责数据的接收和转发,没有太多计算逻辑,所以接入层一般是无状态服务。因此,比如依赖 Nginx + Server 就可以构建一个无状态的集群了。更多细节你可以参考 第15讲。
协议解析,指在集群中适配解析指定协议,比如支持 HTTP 协议或 TCP 协议。从开发的角度看就比较直接,在代码项目中支持两种协议即可,不再赘述。更多细节可以参考 第03讲。
元数据管理,指接入层相关的元数据存在哪里。一般可以用 ZooKeeper 或者 MySQL 来存储。有一点需要注意的是,本地一般需要缓存元数据,以避免接入层频繁访问元数据存储导致元数据存储的压力过大。
集群拆分,指是否对接入层集群进行拆分。即是所有接入层一套集群,还是按照地域分集群,或者给大客户独立搭建集群。从落地的角度,建议一开始一套集群就可以了,但是需要预留拆分集群和独立集群的能力,以便在后续拆分集群,保证服务的可用性和横向扩容能力。
连接管理,指接入层和存储层之间的连接管理。因为接入层需要转发数据到存储层,就需要使用存储层提供的SDK和存储层建立长连接。此时就会存在连接冷启动和空闲连接问题。关于连接管理的细节,你可以参考一下 第07讲。
从技术落地的角度,接入层就是一个经典的无状态服务集群的搭建过程。这节课我们只是宏观上讲了一下模块组成和注意点,如果需要了解更多细节,欢迎在留言区和我讨论。
接下来我们再来简单看一下自定义 SDK 和配套基础设施这两部分。
自定义 SDK 开发
如果使用的是自定义私有协议,那么就需要提供对应的SDK,来降低业务侧的使用成本以及控制用户的使用姿势。从编码的角度来看,它的实现思路和开源SDK的设计思路是一致的,实现细节你可以回顾第07~09讲。
配套基础设施
跟 PaaS 化方案的思路一样,配套基础设施主要是解决的是资源增删改查、配置变更、监控、告警、日志查询等等功能,属于日常的运营系统开发。虽然这块的技术含量不高,但是在实际业务中很重要,需要谨慎对待。
总结
顾名思义,统一的消息服务是收拢企业内部的消息组件,然后提供统一交付、运维、故障处理等服务。从实现上看,有服务 PaaS 化和中台化两种方案。
PaaS 化指由统一的团队提供各种开源的消息队列托管服务。中台化指通过提供统一的接入层负责消息的读写。两种方案的主要区别在于,研发投入的工作量和业务感知的程度。
中台化的研发投入很高,它的优势只有在规模大、对成本和稳定性有强诉求的场景才能更好地体现出来,但它能屏蔽底层细节,让业务专注于使用。PaaS 化的优点是开发成本低,能满足大部分场景。
从技术上看,PaaS 化的主要工作量是平台搭建和成本/稳定性两部分,属于运营平台开发的范畴,技术难度较低。中台化则是由底层存储引擎的选择、接入层协议设计、接入层开发、自定义SDK开发、配套基础设施开发五个部分组成,有一定复杂度。
从实际落地来看,大多数企业的业务形态都不太复杂且用量不大,因此对消息队列的成本和稳定性的诉求没有那么高。所以,对于大部分企业来说,PaaS 化就可以满足大部分场景了。
思考题
在消息中台的接入层,你认为需要支持多协议的接入层吗?从工作量和业务侧需求的角度来看,你是怎么思考这个问题的。
欢迎分享你的想法,如果觉得有收获,也欢迎你把这节课分享给感兴趣的朋友。我们下节课再见!
上节课思考闭环
1. 为什么消息队列集群内容灾不跨地域部署,从而实现地域间的主从切换呢?
集群是可以跨地域部署的,但跨地域的网络延迟一般较高,并且网络稳定性相对本地会低一些。如果集群跨地域部署,则需要控制分区的副本分布在多个不同的地域。当数据写入时,可能会出现请求的耗时较高,或者因为网络抖动、网络延时较高等因素导致数据写入失败。同时网络延时高也会影响消息队列的吞吐。而消息队列作为一个讲究低延时、高吞吐的基础组件,跨地域部署所带来的延时增大、吞吐下降以及偶尔抖动问题是比较难接受的。所以这也是消息队列集群内容灾一般不跨地域部署的原因。
2. Pulsar 支持 Geo Replicated 的策略,请你学习一下它的实现,分析和 Kafka MirrorMaker 在技术上的异同。
请自行参考 Pulsar Geo Replicated 官网,地址: https://pulsar.apache.org/docs/3.1.x/concepts-replication/