Kafka权威指南 读书笔记之(七)构建数据管道
在使用 Kafka 构建数据管道时,通常有两种使用场景 : 第一种,把 Kafka 作为数据管道的两个端点之一 ,例如,把 Kafka 里的数据移动到 S3 上,或者把 MongoDB 里的数据移动
到 Kafka 里;第二种,把 Kafka 作为数据管道两个端点的中间媒介,例如,为了把 Twitter的数据移动到 ElasticSearch 上,需要先把它们移动到 Kafka 里,再将它们从 Kafka 移动到
Elastic Search 上 。
Kafka 为数据管道带来的主要价值在于,它可以作为数据管道各个数据段之间的大型缓冲区, 有效地解桐管道数据的生产者和消费者。 Kafka 的解藕能力以及在安全和效率方面的
可靠性 ,使它成为构建数据管道的最佳选择。
将解释为什么可以使用 Kafka 进行数据集成,以及它是如何解决这些问题的。
讨论 Connect API 与普通的客户端 API (Producer 和Consumer )之间的区别,以及这些客户端 API 分别适合在什么情况下使用 。
然后会介绍 Connnect。 Connect 的完整手册不在本章的讨论范围之内,不过我们会举几个例子来帮助入门,而且会告诉你可以从哪里了解到更多关于 Connect 的信息。
最后介绍其他数据集成系统,以及如何将它们与 Kafka 集成起来 。
构建数据管道时需要考虑的问题
这里不打算讲解所有有关构建数据管道的细节,会着重讨论在集成多个系统时需要考虑的几个最重要的问题。
及时性
有些系统希望每天一次性地接收大量数据,而有些则希望在数据生成几毫秒之内就能拿到它们。大部分数据管道介于这两者之间 。一个好的数据集成系统能够很好地支持数据管道
的各种及时性需求,而且在业务需求发生变更时,具有不同及时性需求的数据表之间可以方便地进行迁移。 Kafka 作为一个基于流的数据平台,提供了可靠且可伸缩的数据存储,
可以支持几近实时的数据管道和基于小时的批处理。生产者可以频繁地向 Kafka 写入数据,也可以按需写入:消费者可以在数据到达的第一时间读取它们,也可以每隔一段时间
读取一次积压的数据。
Kafka 在这里扮演了 一个大型缓冲区的角色,降低了生产者和消费者之间的时间敏感度 。实时的生产者和基于批处理的消费者可以同时存在,也可以任意组合。实现回压策略也因
此变得更加容易, Kafka 本身就使用了回压策略(必要时可以延后向生产者发送确认 ),消费速率完全取决于消费者自己。
可靠性
我们要避免单点故障,并能够自动从各种故障中快速恢复。数据通过数据管道到达业务系统,哪怕出现几秒钟的故障,也会造成灾难性的影响,对于那些要求毫秒级的及时性系统
来说尤为如此。数据传递保证是可靠性的另一个重要因素。有些系统允许数据丢失,不过在大多数情况下,它们要求至少一次传递。也就是说,源系统的每一个事件都必须到达目
的地,不过有时候需要进行重试,而重试可能造成重复传递。有些系统甚至要求仅一次传递一一源系统的每一个事件都必须到达目的地,不允许丢失,也不允许重复 。
Kafka 本身就支持“至少一次传递”,如果再结合具有事务模型或唯一键特性的外部存储系统, Kafka 也能实现“仅一次传递”。
因为大部分的端点都是数据存储系统,它们提供了“仅一次传递”的原语支持,所以基于 Kafka 的数据管道也能实现“仅一次传递”。值得一提的是, Connect APl 为集成
外部系统提供了处理偏移量的 APl ,连接器因此可以构建仅一次传递的端到端数据管道 。
高吞吐量和动态吞吐量
为了满足现代数据系统的要求,数据管道需要支持非常高的吞吐量。更重要的是,在某些情况下,数据管道还需要能够应对突发的吞吐量增长。
由于我们将 Kafka 作为生产者和消费者之间的缓冲区,消费者的吞吐量和生产者的吞吐量就不会辑合在一起了。我们也不再需要实现复杂的回压机制,如果生产者的吞吐量超过了
消费者的吞吐量,可以把数据积压在 Kafka 里,等待消费者追赶上来。通过增加额外的消费者或生产者可以实现 Kafka 的伸缩,因此我们可以在数据管道的任何一边进行动态的伸
缩,以便满足持续变化的需求。
因为 Kafka 是一个高吞吐量的分布式系统, 一个适当规模的集群每秒钟可以处理数百兆的数据,所以根本无需担心数据管道无法满足伸缩性需求。另外 , Connect API 不仅支持伸
缩,而且擅长并行处理任务。稍后,我们将会介绍数据源和数据池( Data Sink)如何在多个线程间拆分任务,最大限度地利用 CPU 资源,哪怕是运行在单台机器上。
Kafka 支持多种类型的压缩,在增长吞吐量时, Kafka 用户和管理员可以通过压缩来调整网络和存储资源的使用。
数据格式
数据管道需要协调各种数据格式和数据类型,这是数据管道的一个非常重要的因素。数据类型取决于不同的数据库和数据存储系统。你可能会通过 Avro 将 XML 或关系型数据加载
到 Kafka 里,然后将它们转成 JSON 写入 ElasticSearch ,或者转成 Parquet 写入 HDFS ,或者转成 csv 写入 S3 。
Kafka 和 Connect API 与数据格式无关。生产者和消费者可以使用各种序列化器来表示任意格式的数据 。 Connect API 有自己的内存对象模型,包括
数据类型和 schema。 不过,可以使用一些可插拔的转换器将这些对象保存成任意的格式,也就是说,不管数据是什么格式的,都不会限制我们使用连接器。
很多数据源和数据池都有 schema,我们从数据池读取 schema,把它们保存起来,并用它们验证数据格式的兼容性,甚至用它们更新数据池的 schema。 从 MySQL 到Hive的数据
管道就是一个很好的例子。如果有人在 MySQL 里增加了一个字段 ,那么在加载数据时,数据管道可以保证 Hive 里也添加了相应的字段。
另外,数据池连接器将 Kafka 的数据写入外部系统,因此需要负责处理数据格式。有些连接器把数据格式的处理做成可插拔的,比如 HDFS 的连接器就支持 Avro 和 Parquet。
通用的数据集成框架不仅要支持各种不同的数据类型,而且要处理好不同数据源和数据池之间的行为差异。例如,在关系型数据库向 Syslog 发起抓取数据请求时, Syslog 会将数据
推送给它们,而 HDFS 只支持追加写入模式,只能向 HDFS 写入新数据,而对于其他很多系统来说,既可以追加数据, 也可以更新已有的数据。
转换
数据转换比其他需求更具争议性。数据管道的构建可以分为两大阵营,即 ETL 和 ELT。
ETL 表示提取一转换一加载( Extract-Transform-Load ),也就是说,当数据流经数据管道时,数据管道会负责处理它们。
这种方式为我们节省了时间和存储空间,因为不需要经过保存数据、修改数据、再保存数据这样的过程。这种方式也有可能给数据管道造成不适当的计算和存储负担。
有一个明显不足,就是数据的转换会给数据管道下游的应用造成一些限制,特别是当下游的应用希望对数据进行进一步处理的时候。假设有人在 MongoDB 和MySQL 之间建立了数据管道,
并且过滤掉了一些事件记录,或者移除了 一些字段,那么下游应用从 MySQL 中访问到的数据是不完整的。如果它们想要访问被移除的字段, 只能重新构建管道,并重新处理历史数据(如果可能的话)。
ELT 表示提取-加载-转换 ( Extract-Load-Transform )。在这种模式下,数据管道只做少量的转换(主要是数据类型转换),确保到达数据池的数据尽可能地与数据源保持一致。 这种
情况也被称为高保真( high fidelity )数据管道或数据湖( data lake )架构。目标系统收集“原始数据”,并负责处理它们。这种方式为目标系统的用户提供了最大的灵活性,因为它
们可以访问到完整的数据。在这些系统里诊断问题也变得更加容易,因为数据被集中在同一个系统里进行处理,而不是分散在数据管道和其他应用里。这种方式的不足在于,数据
的转换占用了目标系统太多的 CPU 和存储资源。有时候,目标系统造价高昂 ,如果有可能,人们希望能够将计算任务移出这些系统。
安全性
对于数据管道的安全性来说,人们主要关心如下几个方面。
• 能否保证流经数据管道的数据是经过加密的?这是跨数据中心数据管道通常需要考虑的一个主要方面。
• 谁能够修改数据管道?
• 如果数据管道需要从一个不受信任的位置读取或写入数据,是否有适当的认证机制?
Kafka 支持加密传输数据,从数据源到 Kafka ,再从 Kafka 到数据池。它还支持认证(通过SASL 来实现)和授权,所以你可以确信,如果一个主题包含了敏感信息,在不经授权的
情况下,数据是不会流到不安全的系统里的。 Kafka 还提供了审计日志用于跟踪访问记录。通过编写额外的代码,还可能跟踪到每个事件的来源和事件的修改者,从而在每个记录之
间建立起整体的联系。
故障处理能力
因为 Kafka 会长时间地保留数据,所以我们可以在适当 的时候回过头来重新处理出错的数据 。
耦合性和灵活性
数据管道最重要的作用之一是解耦数据源和数据池。它们在很多情况下可能发生耦合。
临时数据管道
为每一对应用程序建立单独的数据管道。例如, 他们使用 Logstash 向El asticSearch 导入日志,使用 Flume 向 HDFS 导人日志,使用 GoldenGate 将 Oracle 的
数据导到 HDFS ,使用 Informatica 将 MySQL 的数据或 XML 导到 Oracle ,等等。 他们将数据管道与特定的端点辑合起来,并创建了大量的集成点,需要额外的部署、维护和
监控。当有新的系统加入肘,他们需要构建额外的数据管道,从而增加了采用新技术的成本,同时遏制了创新。
元数据丢失
如果数据管道没有保留 schema 元数据,而且不允许 schema 发生变更,那么最终会导致生产者和消费者之间发生紧密的捐合。没有了 schema ,生产者和消费者需要额外的
信息来解析数据。假设数据从 Oracle 流向 HDFS ,如果 DBA 在 Oracle 里添加了一个字段,而且没有保留 schema 信息,也不允许修改 schema ,那么从 HDFS 读取数据时可能
会发生错误,因此需要双方的开发人员同时升级应用程序才能解决这个问题。不管是哪一种情况,它们的解决方案都不具备灵活性。如果数据管道允许 schema 发生变更,应
用程序各方就可以修改自己的代码,无需担心对整个系统造成破坏。
未端处理
数据管道难免要做一些数据处理。在不同的系统之间移动数据肯定会碰到不同的数据格式和不同的应用场景。如果数据管道过多地处
理数据,那么就会给下游的系统造成一些限制。在构建数据管道时所做的设计决定都会对下游的系统造成束缚,比如应该保留哪些字段或应该如何聚合数据,等等。如果下游
的系统有新的需求,那么数据管道就要作出相应的变更,这种方式不仅不灵活,而且低效、不安全。更为灵活的方式是尽量保留原始数据的完整性,让下游的应用自己决定如
何处理和聚合数据。
如何在Connect API和客户端API之间作出选择
在向 Kafka 写入数据或从 Kafka 读取数据时,要么使用传统的生产者和消费者客户端,要么使用后面即将介绍的 Connect API 和连接器。在具
体介绍 Connect API 之前,我们不妨先问自己一个问题:“什么时候适合用哪一个?”我们知道, Kafka 客户端是要被内嵌到应用程序里的,应用程序使用它们向 Kafka 写入数
据或从 Kafka 读取数据。如果你是开发人员,你会使用 Kafka 客户端将应用程序连接到Kafka ,并修改应用程序的代码,将数据推送到 Kafka 或者从 Kafka 读取数据。
如果要将 Kafka 连接到数据存储系统,可以使用 Connect,因为这些系统不是你开发的, 你不能或者也不想修改它们的代码。 Connect 可以用于从外部数据存储系统读取数据, 或
者将数据推送到外部存储系统。如果数据存储系统提供了相应的连接器,那么非开发人员就可以通过配置连接器的方式来使用 Connect。
如果你要连接的数据存储系统没有相应的连接器,那么可以考虑使用客户端 API 或Connect API 开发一个应用程序。我们建议首选 Connect,因为它提供了一些开箱即用的特性,
比如配置管理、偏移量存储、并行处理、错误处理,而且支持多种数据类型和标准的 REST 管理 API。开发一个连接 Kafka 和外部数据存储系统的小应用程序看起来很简单,
但其实还有很多细节需要处理,比如数据类型和配置选项,这些无疑加大了开发的复杂性一一Connect 处理了大部分细节,让你可以专注于数据的传输。
Kafka Connect
Connect 是 Kafka 的一部分,它为在 Kafka 和外部数据存储系统之间移动数据提供了 一种可靠且可伸缩的方式。它为连接器插件提供了一组 API 和一个运行时一Connect 负责运
行这些插件 , 它们则 负责移动数据。 Connect 以 WO「ke「 进程集群的方式运行,我们基于WO 「 ke 「 进程安装连接器插件,然后使用 REST API 来管理和配置 con nee to「,这些 WO 「 ke 「
进程都是长时间持续运行的作业。连接器启动额外的 task ,有效地利用工作节点的资源,以并行的方式移动大量的数据。数据源的连接器负责从源系统读取数据,井把数据对象
提供给 WO「ke「 进程。数据地的连接器负责从 WO「ker 进程获取数据,井把它们写入目 标系统。 Connect 通过 connecto 「 在 Kafka 里存储不同格式的数据。 Kafka 支持 JSON ,而且
Confluent Schema Registry 提供了 Avro 转换器。开发人员可以选择数据的存储格式,这些完全独立于他们所使用的连接器。
运行Connect
Connect 随着 Kafka 一起发布,所以无需单独安装。如果你打算在生产环境使用 Connect 来移动大量的数据,或者打算运行多个连接器,那么最好把 Connect 部署在独立于 broker 的
服务器上。在所有的机器上安装 Kafka,并在部分服务器上启动 broker,然后在其他服务器上启动 Connect。启动 Connect 进程与启动 broker 差不多 , 在调用脚本时传入一个属性文件即可。