《Kafka权威指南》
  qbian 2020年01月02日 146 0

本书是关于 Kafka 的全面教程,主要内容包括 :Kafka 相对于其他消息队列系统的优点, 主要是它如何完美匹配大数据平台开发 ;详解 Kafka 内部设计 ;用 Kafka 构建应用的最佳实践 ; 理解在生产中部署 Kafka 的最佳方式 ;如何确保 Kafka 集群的安全。

封面

一 初始Kafka

1.1 初始Kafka

1.1.1 消息与批次

Kafka 的数据单元被称为消息。如果你在使用 Kafka 之前已经有数据库使用经验,那么可 以把消息看成是数据库里的一个“数据行”或一条“记录”。消息由字节数组组成,所以 对于 Kafka 来说,消息里的数据没有特别的格式或含义。消息可以有一个可选的元数据, 也就是键。键也是一个字节数组,与消息一样,对于 Kafka 来说也没有特殊的含义。当消 息以一种可控的方式写入不同的分区时,会用到键。最简单的例子就是为键生成一个一致 性散列值,然后使用散列值对主题分区数进行取模,为消息选取分区。这样可以保证具有 相同键的消息总是被写到相同的分区上。

为了提高效率,消息被分批次写入 Kafka。批次就是一组消息,这些消息属于同一个主题 和分区。如果每一个消息都单独穿行于网络,会导致大量的网络开销,把消息分成批次传 输可以减少网络开销。不过,这要在时间延迟和吞吐量之间作出权衡:批次越大,单位时 间内处理的消息就越多,单个消息的传输时间就越长。批次数据会被压缩,这样可以提升 数据的传输和存储能力,但要做更多的计算处理。

1.1.2 模式

对于 Kafka 来说,消息不过是晦涩难懂的字节数组,所以有人建议用一些额外的结构来 定义消息内容,让它们更易于理解。根据应用程序的需求,消息模式(schema)有许多 可用的选项。像 JSON 和 XML 这些简单的系统,不仅易用,而且可读性好。

1.1.3 主题和分区

Kafka 的消息通过主题进行分类。主题就好比数据库的表,或者文件系统里的文件夹。主题可以被分为若干个分区,一个分区就是一个提交日志。消息以追加的方式写入分区,然 后以先入先出的顺序读取。要注意,由于一个主题一般包含几个分区,因此无法在整个主 题范围内保证消息的顺序,但可以保证消息在单个分区内的顺序。图 1-5 所示的主题有 4 个分区,消息被追加写入每个分区的尾部。Kafka 通过分区来实现数据冗余和伸缩性。分 区可以分布在不同的服务器上,也就是说,一个主题可以横跨多个服务器,以此来提供比 单个服务器更强大的性能。

包含多个分区的主题表示

我们通常会使用这个词来描述 Kafka 这类系统的数据。很多时候,人们把一个主题的数 据看成一个流,不管它有多少个分区。流是一组从生产者移动到消费者的数据。当我们讨 论流式处理时,一般都是这样描述消息的。Kafka Streams、Apache Samza 和 Storm 这些框 架以实时的方式处理消息,也就是所谓的流式处理。我们可以将流式处理与离线处理进行 比较,比如 Hadoop 就是被设计用于在稍后某个时刻处理大量的数据。

1.1.4 生产者和消费者

Kafka 的客户端就是 Kafka 系统的用户,它们被分为两种基本类型:生产者和消费者。除 此之外,还有其他高级客户端 API——用于数据集成的 Kafka Connect API 和用于流式处理 的 Kafka Streams。这些高级客户端 API 使用生产者和消费者作为内部组件,提供了高级的 功能。

生产者创建消息。在其他发布与订阅系统中,生产者可能被称为发布者写入者。一般情 况下,一个消息会被发布到一个特定的主题上。生产者在默认情况下把消息均衡地分布到 主题的所有分区上,而并不关心特定消息会被写到哪个分区。不过,在某些情况下,生产 者会把消息直接写到指定的分区。这通常是通过消息键和分区器来实现的,分区器为键生 成一个散列值,并将其映射到指定的分区上。这样可以保证包含同一个键的消息会被写到 同一个分区上。生产者也可以使用自定义的分区器,根据不同的业务规则将消息映射到分 区。

消费者读取消息。在其他发布与订阅系统中,消费者可能被称为订阅者读者。消费者订 阅一个或多个主题,并按照消息生成的顺序读取它们。消费者通过检查消息的偏移量来区 分已经读取过的消息。偏移量是另一种元数据,它是一个不断递增的整数值,在创建消息 时,Kafka 会把它添加到消息里。在给定的分区里,每个消息的偏移量都是唯一的。消费 者把每个分区最后读取的消息偏移量保存在 Zookeeper 或 Kafka 上,如果消费者关闭或重 启,它的读取状态不会丢失。

消费者是消费者群组的一部分,也就是说,会有一个或多个消费者共同读取一个主题。群 组保证每个分区只能被一个消费者使用。图 1-6 所示的群组中,有 3 个消费者同时读取一 个主题。其中的两个消费者各自读取一个分区,另外一个消费者读取其他两个分区。消费 者与分区之间的映射通常被称为消费者对分区的所有权关系

消费者群组从主题读取消息

通过这种方式,消费者可以消费包含大量消息的主题。而且,如果一个消费者失效,群组 里的其他消费者可以接管失效消费者的工作。

1.1.5 broker和集群

一个独立的 Kafka 服务器被称为 broker。broker 接收来自生产者的消息,为消息设置偏移 量,并提交消息到磁盘保存。broker 为消费者提供服务,对读取分区的请求作出响应,返 回已经提交到磁盘上的消息。根据特定的硬件及其性能特征,单个 broker 可以轻松处理数 千个分区以及每秒百万级的消息量。

broker 是集群的组成部分。每个集群都有一个 broker 同时充当了集群控制器的角色(自动 从集群的活跃成员中选举出来)。控制器负责管理工作,包括将分区分配给 broker 和监控 broker。在集群中,一个分区从属于一个 broker,该 broker 被称为分区的首领。一个分区 可以分配给多个 broker,这个时候会发生分区复制(见图 1-7)。这种复制机制为分区提供 了消息冗余,如果有一个 broker 失效,其他 broker 可以接管领导权。不过,相关的消费者 和生产者都要重新连接到新的首领。

集群里的分区复制

保留消息(在一定期限内)是 Kafka 的一个重要特性。Kafka broker 默认的消息保留策略 是这样的:要么保留一段时间(比如 7 天),要么保留到消息达到一定大小的字节数(比 如 1GB)。当消息数量达到这些上限时,旧消息就会过期并被删除,所以在任何时刻,可 用消息的总量都不会超过配置参数所指定的大小。主题可以配置自己的保留策略,可以将 消息保留到不再使用它们为止。例如,用于跟踪用户活动的数据可能需要保留几天,而应 用程序的度量指标可能只需要保留几个小时。可以通过配置把主题当作紧凑型日志,只有 最后一个带有特定键的消息会被保留下来。这种情况对于变更日志类型的数据来说比较适 用,因为人们只关心最后时刻发生的那个变更。

1.1.6 多集群

随着 Kafka 部署数量的增加,基于以下几点原因,最好使用多个集群

  • 数据类型分离
  • 安全需求隔离
  • 多数据中心(灾难恢复)

如果使用多个数据中心,就需要在它们之间复制消息。这样,在线应用程序才可以访问到 多个站点的用户活动信息。例如,如果一个用户修改了他们的资料信息,不管从哪个数据 中心都应该能看到这些改动。或者多个站点的监控数据可以被聚集到一个部署了分析程序 和告警系统的中心位置。不过,Kafka 的消息复制机制只能在单个集群里进行,不能在多 个集群之间进行。

Kafka 提供了一个叫作 MirrorMaker 的工具,可以用它来实现集群间的消息复制。 MirrorMaker 的核心组件包含了一个生产者和一个消费者,两者之间通过一个队列相连。

消费者从一个集群读取消息,生产者把消息发送到另一个集群上。图 1-8 展示了一个使 用 MirrorMaker 的例子,两个“本地”集群的消息被聚集到一个“聚合”集群上,然后将 该集群复制到其他数据中心。不过,这种方式在创建复杂的数据管道方面显得有点力不从心。

多数据中心架构

1.2 为什么选择Kafka

基于发布与订阅的消息系统那么多,为什么 Kafka 会是一个更好的选择呢?

1.2.1 多个生产者

Kafka 可以无缝地支持多个生产者,不管客户端在使用单个主题还是多个主题。所以它很 适合用来从多个前端系统收集数据,并以统一的格式对外提供数据。例如,一个包含了多 个微服务的网站,可以为页面视图创建一个单独的主题,所有服务都以相同的消息格式向 该主题写入数据。消费者应用程序会获得统一的页面视图,而无需协调来自不同生产者的 数据流。

1.2.2 多个消费者

除了支持多个生产者外,Kafka 也支持多个消费者从一个单独的消息流上读取数据,而且 消费者之间互不影响。这与其他队列系统不同,其他队列系统的消息一旦被一个客户端读 取,其他客户端就无法再读取它。另外,多个消费者可以组成一个群组,它们共享一个消息流,并保证整个群组对每个给定的消息只处理一次。

1.2.3 基于磁盘的数据存储

Kafka 不仅支持多个消费者,还允许消费者非实时地读取消息,这要归功于 Kafka 的数据 保留特性。消息被提交到磁盘,根据设置的保留规则进行保存。每个主题可以设置单独的 保留规则,以便满足不同消费者的需求,各个主题可以保留不同数量的消息。消费者可能 会因为处理速度慢或突发的流量高峰导致无法及时读取消息,而持久化数据可以保证数据 不会丢失。消费者可以在进行应用程序维护时离线一小段时间,而无需担心消息丢失或堵 塞在生产者端。消费者可以被关闭,但消息会继续保留在 Kafka 里。消费者可以从上次中 断的地方继续处理消息。

1.2.4 伸缩性

为了能够轻松处理大量数据,Kafka 从一开始就被设计成一个具有灵活伸缩性的系统。用 户在开发阶段可以先使用单个 broker,再扩展到包含 3 个 broker 的小型开发集群,然后随 着数据量不断增长,部署到生产环境的集群可能包含上百个 broker。对在线集群进行扩展 丝毫不影响整体系统的可用性。也就是说,一个包含多个 broker 的集群,即使个别 broker 失效,仍然可以持续地为客户提供服务。要提高集群的容错能力,需要配置较高的复制系数。

1.2.5 高性能

上面提到的所有特性,让 Kafka 成为了一个高性能的发布与订阅消息系统。通过横向扩展 生产者、消费者和 broker,Kafka 可以轻松处理巨大的消息流。在处理大量数据的同时, 它还能保证亚秒级的消息延迟。

1.3 数据生态系统

Kafka 为数据生态系统带来了循环系统,如图 1-9 所示。它在基础设施的各个组件之间传 递消息,为所有客户端提供一致的接口。当与提供消息模式的系统集成时,生产者和消费 者之间不再有紧密的耦合,也不需要在它们之间建立任何类型的直连。我们可以根据业务 需要添加或移除组件,因为生产者不再关心谁在使用数据,也不关心有多少个消费者。

大数据生态系统

二 安装Kafka

2.1 安装Java

Kafka 是使用 Java 开发的应用程序,所以它可以运行在 Windows、MacOS 和 Linux 等多 种操作系统上。

2.2 安装Zookeeper

Kafka 使用 Zookeeper 保存集群的元数据信息和消费者信息。Kafka 发行版自带了
Zookeeper,可以直接从脚本启动,不过安装一个完整版的 Zookeeper 也并不费劲。

Kafka 和 Zookeeper

三 Kafka生产者-向Kafka写入数据

3.1 生产者概览

Kafka 生产者组件图

我们从创建一个 ProducerRecord 对象开始,ProducerRecord 对象需要包含目标主题和要发 送的内容。我们还可以指定键或分区。在发送 ProducerRecord 对象时,生产者要先把键和 值对象序列化成字节数组,这样它们才能够在网络上传输。

接下来,数据被传给分区器。如果之前在 ProducerRecord 对象里指定了分区,那么分区器 就不会再做任何事情,直接把指定的分区返回。如果没有指定分区,那么分区器会根据 ProducerRecord 对象的键来选择一个分区。选好分区以后,生产者就知道该往哪个主题和 分区发送这条记录了。紧接着,这条记录被添加到一个记录批次里,这个批次里的所有消 息会被发送到相同的主题和分区上。有一个独立的线程负责把这些记录批次发送到相应的 broker 上。

服务器在收到这些消息时会返回一个响应。如果消息成功写入 Kafka,就返回一个 RecordMetaData 对象,它包含了主题和分区信息,以及记录在分区里的偏移量。如果写入 失败,则会返回一个错误。生产者在收到错误之后会尝试重新发送消息,几次之后如果还 是失败,就返回错误信息。

3.2 创建Kafka生产者

要往 Kafka 写入消息,首先要创建一个生产者对象,并设置一些属性。Kafka 生产者有 3个必选的属性

1. bootstrap.servers

该属性指定 broker 的地址清单,地址的格式为 host:port。清单里不需要包含所有的 broker 地址,生产者会从给定的 broker 里查找到其他 broker 的信息。不过建议至少要 提供两个 broker 的信息,一旦其中一个宕机,生产者仍然能够连接到集群上。

2. key.serializer

broker 希望接收到的消息的键和值都是字节数组。生产者接口允许使用参数化类型,因 此可以把 Java 对象作为键和值发送给 broker。这样的代码具有良好的可读性,不过生 产者需要知道如何把这些 Java 对象转换成字节数组。key.serializer 必须被设置为一 个实现了 org.apache.kafka.common.serialization.Serializer 接口的类,生产者会使 用这个类把键对象序列化成字节数组。Kafka 客户端默认提供了 ByteArraySerializer
(这个只做很少的事情)、StringSerializer 和 IntegerSerializer,因此,如果你只 使用常见的几种 Java 对象类型,那么就没必要实现自己的序列化器。要注意,key. serializer 是必须设置的,就算你打算只发送值内容。

3. value.serializer

与 key.serializer 一样,value.serializer 指定的类会将值序列化。如果键和值都是字 符串,可以使用与 key.serializer 一样的序列化器。如果键是整数类型而值是字符串, 那么需要使用不同的序列化器。

四 Kafka消费者-从Kafka读取数据

五 深入Kafka

六 可靠的数据传递

七 构建数据通道

八 跨集群数据镜像

九 管理Kafka

十 监控Kafka

十一 流式处理

最后一次编辑于 2021年03月08日 2

暂无评论