Kafka 官方文档学习笔记(一)

引言

好记性不如一个烂笔头。这篇是阅读 Kafka 官方文档时的学习笔记。

介绍

首先,Kafka 是一个分布式流平台(distributed streaming platform)。

我们认为一个流平台要有如下三个关键的能力:

  1. 可以发布消息到记录流(streams of records),可以订阅记录流。在这方面它类似于消息队列(message queue)和企业级消息系统(enterprise messaging system)。
  2. 以容错的(fault-tolerant)方式存储记录流。
  3. 让你可以在记录流出现时就处理它们。

它适合于两大类应用程序:

  1. 构建在系统或应用之间实时可靠地传输数据的流式数据管道(streaming data pipelines)。
  2. 构建实时对数据流(streams of data)进行转换或作出响应的流式应用(streaming applications)。

先了解一下概念:

  • Kafka 就是一个运行在一台或多台服务器上的集群
  • Kafka 对记录流进行分类存储,这里的类别就是 topics
  • 每一条记录由一个 key,一个 value 和一个时间戳组成

四个核心 API:

  • Producer API
  • Consumer API
  • Streams API 可以让一个应用担当流处理器,消费一个[数据来自于一个或多个 topics 的]输入流,并且产生一个[流向一个或多个输出 topics]的输出流,有效地将输入流转换为输出流。
  • Connector API 使我们可以构建并运行可重用的[将 Kafka topics 连接至已存在的应用或者数据系统的]生产者和消费者。例如,一个到关系数据库的连接器可以捕获对表的每一个改变。

核心抽象

Topics and Logs

topic 就是一个记录流(a stream of records)。Kafka 中的 topic 总是可有多个订阅者的(multi-subscriber),也就是说,一个 topic 可以有 0、1,或许多个消费者来订阅写入其中的数据。

对于每个 topic, Kafka 集群维护着一个被分割成若干分区(partitioned)的日志,看起来像这样:

每个分区就是一个有序的,不可变的记录序列;它不断地被附加到一个结构化的提交日志中。分区中的每一条记录都被分配了一个顺序 id 编号,称它为 offset。在一个分区中它可以唯一地标示每一条记录。

PS: 分区数量是创建 topic 时就配置好的,还是可以运行时不断地产生新的分区?

Kafka 集群会保留所有已发布的记录——不管它们是否已经被消费——使用一个可配置的保留时间(retention period)。到期的数据会被丢弃以释放空间。关于数据大小,Kafka 的性能实际上是稳定不变的,不会因数据大小变化而变化,因此长期存储数据不是个问题。

事实上,在每个消费者上保留的仅有的元数据就是这个消费者在日志中的 offset 或叫 position。这个 offset 由消费者自己控制:
正常情况下,随着它不断地读取记录,它的 offset 就会不断向前移动。但实际上,它可以以任何它喜欢的顺序来消费记录。例如消费者可以重置到一个更老的 offset 以消费过去的记录,或者向前跳至最近的记录,从“现在”开始消费。

这个特性组合意味着 Kafka 消费者非常便宜——它们可以时来时去而不会对进群或其他消费者造成太多影响。例如,你可以使用命令行工具去 tail 任何 topic 的内容而不会改变被任何已存在的消费者消费的内容。

对日志进行分区服务于几个目的。首先,这允许日志扩展到超过单台服务器所能存储的大小。虽然每个单独的分区必须能装进存贮它的主机,但是一个 topic 可以有很多分区,使得它能够处理任何数量的数据。其次,分区扮演着并行单元(unit of parallelism)的角色——稍后进一步解释(more on that in a bit)。

Distribution

分区被分布到集群服务器上,每台服务器都处理数据和对其中一个分区请求。为了容错,每个分区被复制到可配置数量的服务器上。

每个分区都有一台服务器扮演 leader,0 或多台服务器扮演 followers。leader 处理所有对分区的读写请求而 followers 只是被动从 leader 复制。 如果 leader 失败,其中一个 follower 将自动变成新的 leader。每台服务器担任了一些分区的 leader,同时也担任了其他一些分区的 follower,因此负载很好地均衡给了集群中的每台服务器。

Producers

生产者负责决定将哪条记录分派给 topic 中的哪个分区。可以简单地使用 round-robin 方式来负载均衡,或者使用某个有语义的分区函数(比方说,基于记录中的某个 key)。

Consumers

消费者给它们自己贴上消费者分组名称的标签。发布到的 topic 中的每一条记录只会被投递给每一个订阅此 topic 的消费者分组中的一个消费者实例。

  • 如果所有消费者实例拥有同一个分组,那么记录将在所有消费者实例上被负载均衡
  • 如果所有消费者实例拥有不同的分组,那么每一条记录将被广播给所有的消费者进程。批注:原文是:If all the consumer instances have different consumer groups, then each record will be broadcast to all the consumer processes. 感觉有问题,正确的描述应该是:每一条记录将被广播给所有的消费者分组(实际上每个分组中只有一个实例消费到)

一个包含两台服务器的集群,存贮了4个分区(p0-p3),分成 A、B 两组

更常见的是,topic 有小数量的消费者分组,每个分组就是一个逻辑订阅者(logical subscriber)。每个分组有许多消费者实例,为了可扩展性和容错。此时,这里的订阅者是一个消费者集群,而不是单个进程。

Kafka 将日志中的分区分配给一个分组中的所有实例,使得每一个实例在任何时间点都是一份公平的分区的专有消费者。Kafka 将这个负载均衡策略应用于每一个分组。对分组中成员关系的维护由 kafka 协议动态地处理了。如果有新实例加入分组,它们就会从其他成员接手某些分组;如果一个实例死掉了,它的分区就会被分配给剩余实例。

Kafka 只提供一个分区内所有记录的总体有序性,并不提供 topic 中不同分区间的全局有序性。如果你需要所有记录全局有序,可以使用只有一个分区的 topic 来实现,然而这意味着每个消费者分组中也只有一个实例。

Kafka 能做什么

Kafka as a Messaging System

将 Kafka 的流概念与传统企业消息系统进行比较。

传统上消息化有两种模型:队列和发布-订阅。在队列模型中,一群消费者从服务器读取记录,并且每条记录只会投递给其中一个消费者;在发布-订阅中,每条记录会广播给所有消费者。队列的优点是它让你可以将数据的处理分配给多个消费者实例,这让你可以扩展处理能力。不幸的是,队列不支持多个订阅者——一旦某个消费者读取了一条数据,它就不存在了。发布-订阅让你可以将数据广播给多个消费者,但没法扩展处理能力,因为每条消息都要投递给每一个订阅者。

Kafka 的 消费者分组 概念概括了这两个概念。和队列一样,消费者组可以让你将数据处理分配给组中的成员。和发布-订阅一样,Kafka 让你可以将消息广播给多个消费者组。这样,Kafka 的 topic 既可以扩展处理能力,又支持多个订阅者,无需在两者之中取其一。

跟传统消息系统相比,Kafka 有更强的排序保证。

传统队列虽然在服务器上保持消息全局有序,但是当多个消费者并行消费时,消息到达消费者的顺序很可能已经失序了。变通的解决办法是让一个排它性的消费者(exclusive consumer)消费一个队列,这又导致没法并行处理。

这点 Kafka 做得更好,它有一个并行的概念——分区。分区既能提供有序性保证,又提供在一组消费者实例上负载均衡的能力。通过让一个 topic 的每个分区 在订阅此 topic 的一个消费者分组中 只会被一个消费者实例消费就实现了这点。这样就确保了在那个分组中那个消费者就是那个分区唯一的读取者,它就能有序地消费数据了。注意,一个消费者分组中的实例数不能大于分区数!

Kafka as a Storage System

作为发送中的消息(in-flight messages)的存储系统,Kafka 将数据写入磁盘,且为了容错进行了复制。Kafka 允许生产者等待确认(acknowledgement),为的是即使要写入的服务器失败,一个写请求在被完全复制和被保证持久化之前,这个写请求不会被认为是已完成的。

Kafka 使用的磁盘结构可以很好地扩展——服务器上存了 50 KB 或 50 TB 的持久化数据,Kafka 运转起来一样,性能不会变化。

认真对待存储和允许消费者控制自己的读取位置(即 offset)使得 Kafka 可以作为一种致力于高性能,低延时的提产日志(commit log)的存储、复制和传播(propagation)的分布式文件系统。

Kafka for Stream Processing

只是读,写,存储数据流还不够,目的是实现对数据流的实时处理。

在 Kafka 中,从输入 topics 中获取连续的数据流,并对这些输入做些处理,然后产生连续的数据流并发布到输出 topics 的任何东西都是一个流处理器。

对于很复杂的转换,Kafka 提供了一个综合完整的 Streams API。简单的处理可以使用 Producer 和 Consumer APIs。

Putting the Pieces Together

Kafka 既有可靠且容量可扩展的存储,又有低延时的消息订阅。因此,基于 kafka 构建的应用既可以存储和(批量)处理大量的历史数据,又可以处理将来的数据(订阅后等待数据到达)。

Use Cases

受欢迎的使用案例。有关实践中这些领域的概述,请看这篇博文 The Log: What every software engineer should know about real-time data’s unifying abstraction

Messaging

在我们的经验中,作为消息系统使用时吞吐量的要求会相对低一点,但可能要求低的端到端延时(end-to-en latency),且经常要依赖 Kafka 提供的强健的持久化保证。

传统消息系统:ActiveMQRabbitMQ

Website Activity Tracking

跟踪 web 站点在页面上的活动(如页面浏览,搜索等)。一般每种活动类型一个 topic。

Metrics

运营监控数据的采集和分析。从分布式应用聚合各种统计数据,然后生成中心化的运营数据 feeds。

Log Aggregation

作为日志聚合方案的一个替代。典型地,日志聚合从服务器收集物理日志文件,然后将它们放到一个中心存储(文件服务器或者 HDFS)以供分析。Kafka 拿掉了有关文件的细节,提供了一个更干净的抽象——将日志或事件数据看作消息流。

Stream Processing

在多阶段组成的处理管道中处理实时数据流。所谓多阶段,指的是从一个 topic 中消费数据,经过聚合/合计,充实丰富,或者转换再发布到另一个 topic 以供后续处理,这个过程可能会重复多次。

可作为替代的开源流式处理工具:Apache StormApache Samza

Event Sourcing

Event Sourcing 是一种应用架构风格,把应用状态的改变记录为按时间排序的事件序列。Kafka 可以存储海量的日志数据。

Commit Log

Kafka 可用作分布式系统的一种外部提交日志(commit-log)。提交日志帮助在节点之间复制数据,并且用作失败节点用来恢复数据的一种重新同步(re-syncing)的机制。Kafka 的 log compaction 特性支持这种使用场景。

类似开源项目:Apache BookKeeper

参考资源

Kafka official docs

0%