AI前线导读: 消息中间件系统(比如RabbitMQ、Kafka、Pulsar等)是现代实时数据或者流数据基础架构的关键环节。它通常作为一个数据管道,链接了各种业务前台和数据后台(比如数仓等)。但是随着越来越多的企业应用开始采纳流计算作为数据的计算引擎,以及基础架构的容器化、云化和无服务器化(Serverless),这些消息系统也随之发生变化,逐步向流数据平台演进。在这篇文章中,笔者根据自己在这个领域从业多年的经验,详细解读消息系统的演化历史,不同消息系统在基础设施变革的浪潮中面临的挑战,以及他们相应的优势和劣势,并对消息系统怎么跟已有的计算框架进行整合,以及对未来可能的发展方向进行探讨。

本文是InfoQ特别策划《Kafka的七年之痒》专题系列文章的第二篇,第一篇文章回顾见《Kafka从0.7到1.0:过去7年我们踩过哪些坑?》

更多干货内容请关注微信公众号“AI前线”(ID:ai-front)

1. 消息系统的演化历史

杠上Spark、Flink?Kafka为何转型流数据平台

消息系统作为一个核心的基础架构组件由来已久,而且运用广泛。整个消息系统的演化进程,大致可以分为三个阶段:

  • 1.0时代:JMS以及各种MQ

  • 2.0时代:Kafka的实时管道时代

  • 3.0时代:流数据平台时代 (Kafka和Pulsar)

1.0 - MQ时代

消息系统作为一个基础机构的组件,通常用于连接不同的软件服务。这些服务可以相互连接,形成一个更大的服务。或者,它也用于将用户设备和后台服务进行连接。消息系统通过将消息的分发和接收分离来实现应用服务的异步和解耦。

或许你正在考虑进行数据投递、非阻塞操作,或者推送通知;或者你想要实现发布/订阅,异步处理,或者工作队列。所有这些都属于消息系统的模式。这些消息系统通过JMS或者AMQP的消息规范或者协议进行通讯。比如RabbitMQ就是AMQP的一个消息系统实现。它可以为你的应用服务提供一个通用的消息发送和接收平台,并且保证消息在传输过程中的安全。

举个例子,对于一个大型的系统来说,它通常会由很多不同的组件或者子系统构成。如果这些子系统直接使用传统的IPC或者Socket网络通讯构建,那么模块和子系统之间的耦合性会很大,并不适合进行扩展;而且它同时需要考虑各种问题——比如数据的发送方和接收方怎么进行容错处理,如何做负载均衡,如何处理系统扩展等。而一个消息系统就可以解决上述所有问题。

在这个时代,消息系统主要以围绕JMS、AMQP等标准化的消息规范和消息协议设计的系统实现为代表,比如ActiveMQ,RabbitMQ等。消息系统在这个时代主要用于在线业务,用来解耦系统的复杂度。

2.0 - 实时管道时代

消息系统演化的2.0时代,其实是一个实时管道的时代。而Kafka是这个时代的典型代表系统。Kafka是2010年左右在Linkedin研发的一套分布式消息系统。当时的Linkedin和很多互联网公司一样,分很多的组,有很多的数据产品,每天需要采集非常多的数据。这些数据都是由不同数据源实时生成,比如用户活跃度、日志等。如果数据的生产者和消费者之间采用点对点的方式进行数据传输,那么运维的人力和物力成本就会很高。于是Linkedin需要一个集中式的数据管道,所有的业务方都只要跟这个数据管道打交道就可以,不再需要进行点对点的数据传输。

从2010年开始,Linkined尝试了不同的消息系统。但是发现1.0时代的消息系统都有两个比较通用的缺陷:一是当消费者出现,无法及时消费数据的时候,消息数据可能会被丢弃;二是可扩展性上,并不能很好的配合互联网的数据规模。Kafka就诞生在这样的背景下。

Kafka的设计理念很简单,就是一个以append-only日志作为核心的数据存储结构。简单来说,就是Kafka把数据以日志的方式进行组织,所有的数据以追加日志的方式写到日志的最末端,对日志的读取按照顺序进行读取。这样尽可能讲数据的读写按照顺序进行操作,这样可以做到比传统MQ更高的吞吐。此外,数据以Topic为单位作为粒度,按照分区进行切分,存储在不同的服务器上。数据的发布和订阅都基于Topic,数据更新时,消费端的客户端会把它们从服务器上拉去下来。

Kafka变得流行,并且成为那个时代的数据管道,得益于Storm的流行。Storm的兴起和Lambda架构的引入弥补了Hadoop大数据生态在速度和时延上的短板。大量的互联网公司比如Twitter等,开始使用Storm和Lambda架构,Kafka的实时管道特性,配合Storm的流计算,使之开始变得流行。

3.0 - 流数据平台时代

消息系统演化的第三个阶段是流数据平台。这包含两方面的含义,其一是流数据,其二是平台化。流数据,广义上来讲,是相对于批处理时代的静态数据而言的。这其中包括微服务、事件驱动架构(Event-Driven-Architecture)的流行,物联网的兴起等。而平台化意味着消息系统需要能够作为一个平台系统去支撑不同的业务服务、不同的租户管理,而不再是一个简单的数据管道。Apache Pulsar就是新一代消息系统的代表。

这些系统的诞生,主要与以下几个因素有关:

首先,传统的消息系统比如各种MQ和Kafka并不能很好地支持平台化,或者随着数据规模的增长,业务负载多样性的增加,这些系统开始暴露大量问题:基本上传统的消息系统都是以分区为主的架构设计,紧耦合了消息服务(计算)和消息存储,而且存储模型都过于简单或者太依赖于文件系统。随着Topics数据量的增加,或者数据重要性(不丢数据)的加强,这些系统的性能会急剧下降。

其次,基础架构的容器化。从2012年开始,Mesos的流行、Docker的兴起,到现在Kubernetes一统天下,整个基础架构正在全面往容器化发展。任何紧耦合计算和存储的架构并不能很好地使用新的容器化架构。消息系统需要一个计算和存储相互分离的架构设计去更好地适应容器化的变革。

第三,基础架构的云化。云化是一种新的思维方式。首先,不论是公有云还是私有云,架构设计都需要考虑平台化,也就是多租户、IO隔离、流控、配额以及安全开始变成消息系统的标配;其次,架构设计需要考虑如何去使用云资源(比如云存储等)。

第四,计算框架的批流一体化。无论是Flink还是Spark,流计算还是批计算的边界已经变得模糊。用户真正关心的是如何更好更快地使用数据,如何从数据中更快地挖掘出其中的价值。而这其中最核心的思维转变是,流数据和静态数据不再是不同的数据,它们其实是同一份数据的两种不同表征方式。

第五,计算轻量化,Serverless和事件驱动架构带来的变革。

2. Kafka的挑战

正如上文所述,Kafka基本上是当下实时管道的第一选择。在Kafka 0.8之后,Kafka也在往平台化的方向发展。现在的Kafka除了最核心的消息发布和订阅之外,还包括了以下一些新兴组件,比如:

  • Kafka Connect:用来从Kafka导入和导出数据

  • Kafka Streams:轻量化的流计算库,用于编写一些简单的计算任务处理Kafka的数据。

  • 此外,还包含Schema Registry、KSQL等组件。

但是,Kafka在平台化的过程中,最核心的挑战在于其架构如何适应云原生的挑战。

首先,Kafka以分区为中心的架构设计是面向物理机时代的架构设计。它紧耦合了消息服务(计算)和消息存储,Kafka的分区跟一台或者一组物理机强绑定。强绑定带来的问题是,当处理机器失效或者扩容的过程中,Kafka需要进行昂贵且缓慢的分区数据重新均衡的过程。这个过程十分漫长,而且容易出错。一旦出错,可能带来服务的不可用性。

其次,Kafka以分区为粒度的存储设计,导致其并不能很好地利用已有的云存储资源。

最后,Kafka的存储设计过于简单,导致其进行多租户管理、IO隔离以及平台化转型过程中,需要解决架构上的很多缺陷。

3. Pulsar的云原生之路

而近一年多崛起并渐渐被更多开发者了解的Apache Pulsar,与Apache Kafka的不同也正好体现在云原生架构设计上。Apache Kafka在设计上的一些并不能很好地适应于云原生环境的缺陷,比如消息服务和消息存储的紧耦合、IO并不隔离、基于物理分区的存储模型等,Apache Pulsar在设计之初就很好地避开了——比如计算和存储分离、分层分片、IO隔离、多租户管理等。

Apache Pulsar是2012年在Yahoo内部启动的项目。其最初的设计,就是奔着做Yahoo内部的消息云去做的。所以Pulsar从写第一行代码开始,就把租户的概念做进去了,并吸取了以前系统的经验和教训,避免了以前的系统设计上的缺陷。Pulsar在生产线上成功运行了4年后,在2016年九月由Yahoo开源,并在2017年六月捐献给Apache软件基金会。Pulsar在今年九月成功毕业成为*项目。从开始孵化到最终毕业,总共经历了9个releases,目前社区总共有23位committers,30多家公司将Pulsar运行在生产线上。

Apache Pulsar作为新兴的消息流数据平台,除了拥有丰富的特性(比如多租户管理,IO隔离,多机房复制等)之外,它跟传统的消息系统最大的不同是,Pulsar是一个面向容器化设计的云原生的流数据系统。怎么来理解这个问题呢?

首先,整个IT的基础设施是从传统的物理机模型往容器化模型迁移。容器化对于架构设计的直接影响,就是将原来一体化(Monolithic)的架构按照处理逻辑拆分成小的逻辑单元,并进行容器化。对于分布式系统的设计的影响,通常体现在计算和存储的分离。存储和计算的分离通常应用在一些新型的数据库系统,比如TiDB。Pulsar正是在这种容器化进程中诞生的。Pulsar将系统分为两层,一层是无状态的消息服务(计算)层——Brokers,另外一层是持久化的消息存储层——Bookies (via Apache BookKeeper)。计算和存储分离之后,两层可以相互独立扩展,如果需要存储更多的数据,只需要添加存储节点;如果需要支持更多的生产者和消费者,只需要添加Brokers。此外,因为Brokers变成了一个无状态的服务组件,容错处理变得更加容易,从而能够极速扩容。

其次,基础架构的云化,使得用户更加容易在云上得到弹性的计算资源和存储资源。以存储资源为例,AWS有S3,Azure有Blob Store,而GCP有GCS。传统的面向物理分区模型设计的系统,并不能很好地利用云存储资源。而Pulsar在存储上做了一个降维的处理。Pulsar把物理分区变成了逻辑分区,而将存储粒度从粗粒度的分区变成了细粒度的分片(Segment)。因此Pulsar可以将消息以分片的粒度存储在不同的云存储中,而向外部使用者依然提供统一的消息模型。这种分片的架构,更加原生地利用云存储资源。

再次,计算框架的批流一体化,意味着消息和存储之间是共性的。消息的数据是流入系统的最新数据,而这些数据落到存储上就变成了”历史“数据,并用于批量计算。而Pulsar将数据的消息和存储共性体现在分层和分片的处理上,消息服务层(Brokers)用来提供消息的Pub-Sub,用于流式计算;而消息落地到存储层,按照分片存储,则可以进行批式计算。而这种消息和存储的共性,让用户不在需要区分这个数据是消息数据还是历史数据,从而做到真正意义上的批流一体化。

最后,基础架构的演变从物理机,到虚拟机,再到容器,以及到现在的Serverless。计算资源的粒度变得越来越细,用户在使用计算资源的过程中,变得越来越关注于计算的本身。这也是所谓计算轻量化的发展之道。Pulsar在2.0之后,将Serverless的概念引入了流数据平台,变成了所谓的Pulsar Functions。Functions的诞生就是为了让用户更加专注于编写事件处理逻辑。

4. 轻量化计算和Spark、Flink

在大数据计算的领域,Spark和Flink都是通用的能够支持超大规模数据处理、支持各种处理类型的计算引擎。Spark从2014年左右开始流行,除了在某些场景比Hadoop MapReduce带来几十到上百倍的性能提升之外,还提出了用一个统一的引擎支持批处理、流处理、交互性查询、机器学习等常见的数据处理场景。而Flink则是在2016年左右开始进入大众的视野,并凭借其更优的流处理引擎,批流一体计算等逐渐广为人知,同时也支持各种处理场景,成为Spark的有利挑战者。

但是,随着微服务的兴起,以及事件驱动架构的流行,大家慢慢发现,为了编写一些简单计算而去部署一套Flink或者Spark,代价有点大,有种杀鸡用牛刀的感觉。于是,大家开始琢磨怎么能够更加简化这些计算,并开始在消息系统上添加轻量化计算。Kafka引入了KStreams,使用了传统的流计算的概念,只是将计算变得轻量化,不再依赖于某个计算平台,用户可以选择自己最适合的部署方式;而Pulsar则走了截然不同的一条路径,它跳脱出了传统流计算的模型,而借鉴了Serverless的概念,将Serverless Function引入了消息系统内部。用户可以通过编写原生的Function来进行任意逻辑的计算。以Function为主导的轻量化计算让用户更加关注于计算逻辑本身,适用于一些简单计算,比如Filtering、Aggregation、Routing等。

数据处理的意义就是挖掘蕴含在数据内部的价值,而且Spark和Flink是通用计算引擎的两个巨头,基于消息系统衍生出来的轻量化计算并不是一种通用计算,不能与已有的通用计算引擎抗衡。但轻量化计算是对于通用计算的一种补充,让一些微服务的构建以及事件驱动架构的设计变得更加容易。这些消息流平台在通用计算方面,还是需要跟Spark和Flink更加紧密地结合。

5. 未来展望

消息系统作为大数据基础架构的一个环节,起着至关重要的作用它们也随着基础设施的演化而不断进步。如何更好地使用云化和容器化的基础设施,将是每个消息系统面临的挑战。批流一体化和统一的数据表征,也是下一台数据平台需要支持的特性。

作为数据平台,如何更好地跟已有的计算框架比如Flink和Spark结合,进行批流一体的计算?如何权衡轻量化计算和复杂计算的边界?不论是Kafka还是Pulsar,都还任重而道远。

分类:

技术点:

相关文章: