介绍
Apache Kafka®是一个分布式流平台,这到底是什么意思?
流平台有三个关键功能:
- 发布和订阅记录流,类似于消息队列或企业消息传递系统
- 以容错持久的方式存储记录流
- 在记录流发生的时候处理它们
Kafka通常用于两大类应用程序:
- 构建实时流数据管道,在系统或应用程序之间可靠地获取数据
- 构建对数据流进行转换或响应的实时流应用程序
要了解Kafka是如何完成这些事情的,让我们从底层深入探究Kafka的能力。
第一步了解几个概念:
- Kafka作为集群运行在一个或多个可以跨多个数据中心的服务器上
- Kafka集群以主题的类别存储记录流
- 每个记录由一个键、一个值和一个时间戳组成
Kafka有四个核心API:
- 生产者API允许应用程序将记录流发布到一个或多个Kafka主题
- 消费者API允许应用程序订阅一个或多个主题并处理生产给它们的记录流
- 流API允许应用程序充当流处理器,消费来自一个或多个主题的输入流,并生产输出流到一个或多个输出主题,有效地将输入流转换为输出流。
- 连接器API允许构建和运行可重用的生产者或消费者,将Kafka主题与现有的应用程序或数据系统连接起来,例如,连接关系数据库的连接器可能会捕获对表的所有更改。
在Kafka中,客户机和服务器之间的通信是使用简单的、高性能的、与语言无关的TCP协议完成的,该协议是版本化的,并与旧版本保持向后兼容性,我们为Kafka提供一个Java客户端,但是客户端可以使用多种语言。
主题和日志
让我们首先深入讨论Kafka为记录流提供的核心抽象——主题。
主题是发布记录的类别或提要名称,Kafka中的主题总是多订阅者,也就是说,一个主题可以有0个、1个或多个订阅写入到它的数据的消费者。
对于每个主题,Kafka集群维护一个类似于这样的分区日志:
每个分区都是一个有序的、不可变的记录序列,并不断地附加到一个结构化的提交日志中,分区中的记录都被分配了一个名为偏移量的连续id号,该偏移量惟一地标识分区中的每个记录。
Kafka集群可以持久地保存所有已发布的记录—无论它们是否被消费—并使用可配置的保留期。例如,如果保留策略被设置为两天,那么在记录发布后的两天内可以消费它,然后将其丢弃以释放空间,Kafka在数据大小方面的性能实际上是恒定的,因此长时间存储数据不是问题。
事实上,在每个消费者的基础上保留的唯一元数据是该消费者在日志中的偏移量或位置,这个偏移量由消费者控制:通常消费者会在读取记录时线性地增加其偏移量,但是,实际上,由于位置是由消费者控制的,它可以按照自己喜欢的顺序消费记录。例如,消费者可以重置到一个较早的偏移量来重新处理过去的数据,或者跳转到最近的记录并从“现在”开始消费。
这种特性的组合意味着Kafka消费者非常廉价——他们可以来来去去,不会对集群或其他消费者造成太大影响。例如,你可以使用我们的命令行工具“跟踪”任何主题的内容,而无需更改任何现有消费者消费的内容。
日志中的分区提供有几个用途。首先,它们允许日志扩展到超出适合单个服务器的大小,每个单独的分区必须适合承载它的服务器,但是一个主题可能有许多分区,因此它可以处理任意数量的数据,第二,它们作为并行的单位——稍后再进一步探讨。
分布
日志的分区分布在Kafka集群中的服务器上,每个服务器处理数据和共享分区的请求,每个分区在可配置的服务器数量上复制,用于容错。
每个分区都有一个充当“leader”的服务器和一个或多个充当“followers”的服务器,leader处理分区的所有读写请求,而follower被动地复制leader,如果leader故障了,followers中的一个将自动成为新的leader,每个服务器作为它的一些分区的leader,而作为其他分区的follower,所以集群中的负载是很平衡的。
地理复制
Kafka MirrorMaker为集群提供地理复制支持,使用MirrorMaker,消息可以跨多个数据中心或云区域复制,你可以在主动/被动场景中使用它进行备份和恢复,或者在活动/活动场景中,将数据放置到离用户更近的位置,或者支持数据局部性需求。
生产者
生产者将数据发布到他们选择的主题,生成器负责选择要将哪个记录分配给主题中的哪个分区,这可以以循环方式完成,只是为了平衡负载,也可以根据一些语义分区函数(比如基于记录中的某个键)完成,使用分区的更多信息参见第二章!
消费者
消费者用消费者组名称来标记自己,并且将发布到主题的每个记录都被传递到每个订阅消费者组中的一个消费者实例,使用者实例可以在单独的进程中,也可以在单独的机器上。
如果所有消费者实例都有相同的消费者组,那么记录将有效地在消费者实例上进行负载均衡。
如果所有消费者实例都有不同的消费者组,那么每个记录将被广播到所有消费者进程。
一个负载四个分区(P0-P3)和两个消费者组的两个Kafka集群服务器,消费者组A有两个消费者实例,而B组有四个。
然而,更常见的是,我们发现主题有少量的消费者组,每个“逻辑订阅者”对应一个消费者组,每个组都由许多消费者实例组成,以实现可扩展性和容错,这只不过是发布-订阅语义,其中订阅服务器是一个消费者集群,而不是一个进程。
在Kafka中实现消费的方式是在消费者实例中划分日志中的分区,以便每个实例在任何时候都是“公平共享”分区的唯一消费者,维持组中成员资格的过程是由Kafka协议动态处理的,如果新实例加入组,它们将从组的其他成员接管一些分区,如果一个实例消亡,那么它的分区将被分配给其他实例。
Kafka只对一个分区内的记录提供总的顺序,而不是在主题中的不同分区之间,对大多数应用程序来说,每个分区排序结合按键对数据进行分区的能力就足够了,但是,如果你需要记录上的总顺序,这可以通过只有一个分区的主题实现,尽管这意味着每个消费者组只有一个消费者进程。
多租户
你可以将Kafka部署为多租户解决方案,通过配置哪个主题可以生产或消费数据来启用多租户,也有对配额的操作支持。管理员可以对请求定义和强制配额,以控制客户端使用的代理资源,有关更多信息,请参见安全文档。
保证
在高级Kafka中提供以下保证:
- 生产者发送给特定主题分区的消息将按照发送的顺序附加,也就是说,如果一个记录M1是由与记录M2相同的生产者发送的,而M1是先发送的,那么M1的偏移量将小于M2,并出现在日志的前面。
- 一个消费者实例会根据存储在日志中的顺序来查看记录。
- 对于复制因子N的主题,我们可以容忍最多N-1个服务器失败,而不会丢失提交给日志的任何记录。
有关这些保证的更多细节将在文档的设计部分中给出。
Kafka作为一个消息系统
Kafka的流概念与传统的企业消息传递系统相比如何?
消息传递传统上有两种模式:队列和发布-订阅。在队列中,消费者池可以从服务器读取数据,每个记录都将被发送到其中一个,在发布-订阅中,记录被广播给所有的消费者,这两种模式都有优点和缺点。队列的优势在于,它允许你在多个消费者实例上划分数据处理,从而使你能够扩展处理,不幸的是,队列不是多订阅的—一旦一个进程读取了数据,数据将消失。发布-订阅允许你向多个进程广播数据,但是由于每个消息都传递给每个订阅服务器,因此没有扩展处理的方法。
Kafka中的消费者组概念概括了这两个概念,与队列一样,消费者组允许你将处理划分为多个进程集合(消费者组的成员),与发布订阅一样,Kafka允许你向多个消费者组广播消息。
Kafka模型的优点是每个主题都具有这两种特性——它可以扩展处理并且也是多订阅——不需要选择其中之一或另一个。
与传统的消息传递系统相比,Kafka也有更强的顺序保证。
传统队列在服务器上保留记录的顺序,如果多个消费者从队列中消费,那么服务器按存储记录的顺序分发记录,然而,尽管服务器按顺序分发记录,但记录是异步交付给消费者的,因此它们可能在不同的消费者上以无序的方式到达,这实际上意味着记录的顺序会在并行使用时丢失。消息传递系统通常是通过“独占消费者”的概念来工作的,它只允许一个进程从队列中消费,但这当然意味着在处理过程中没有并行性。
Kafka是更好的,通过将并行性的概念划分为主题内的分区,Kafka能够在消费者进程池中同时提供顺序保证和负载平衡。这是通过将主题中的分区分配给消费者组中的消费者来实现的,这样每个分区都由组中的一个消费者使用,通过这样做,我们确保消费者是该分区的唯一读取者,并按顺序消费数据,由于有许多分区,这仍然平衡了许多消费者实例的负载,但是请注意,消费者组中的消费者实例不能超过分区。
Kafka作为一个存储系统
任何允许发布消息与消费消息分离的消息队列都有效地充当了正在运行的消息的存储系统,Kafka的不同之处在于它是一个非常好的存储系统。
写入Kafka的数据被写入磁盘并复制以获得容错,Kafka允许生产者等待确认,直到完全复制并保证即使被写入的服务器发生故障也能保证写入,才认为写入是完整的。
Kafka将磁盘结构使用的很好—无论你在服务器上有50KB或50TB的持久性数据,Kafka都将执行相同的操作。
由于认真对待存储,并允许客户端控制其读取位置,你可以将Kafka视为一种专用于高性能、低延迟提交日志存储、复制和传播的分布式文件系统。
有关Kafka的提交日志存储和复制设计的详细信息,请阅读设计章节。
Kafka的流处理
仅仅读取、写入和存储数据流是不够的,其目的是使流的实时处理成为可能。
在Kafka中,流处理器可以从输入主题中获取连续的数据流,对这个输入执行一些处理,并生成连续的数据流到输出主题。
例如,零售应用程序可能会接收销售和出货的输入流,并输出从该数据计算的重新排序和价格调整的流。
可以直接使用生产者和消费者API进行简单的处理,然而,对于更复杂的转换,Kafka提供一个完整的流API,这允许构建应用程序,这些应用程序进行不是一般的处理,从流中计算聚合或将流连接到一起。
该工具有助于解决此类应用程序面临的难题:处理无序数据,重新处理输入作为代码变更,执行有状态计算等。
流API建立在Kafka提供的核心原语之上:它使用生产者和消费者API进行输入,使用Kafka进行有状态存储,并在流处理器实例中使用相同的组机制进行容错。
把碎片拼在一起
这种消息传递、存储和流处理的组合看起来可能不太常见,但对于Kafka作为流平台的角色来说是非常重要的。
像HDFS这样的分布式文件系统允许为批处理存储静态文件,这样的系统可以有效地存储和处理过去的历史数据。
传统的企业消息传递系统允许处理订阅后将到达的未来消息,以这种方式构建的应用程序在数据到达时处理它们。
Kafka将这两种功能结合起来,对于Kafka作为流应用程序的平台以及流数据管道来说,这种组合非常关键。
通过结合存储和低延迟订阅,流应用程序可以以相同的方式处理过去和未来的数据,这是一个单一的应用程序可以处理历史的、存储的数据,但是当它到达最后一个记录时,它可以继续处理,因为将来的数据会到达,这是流处理的广义概念,它包括批处理和消息驱动应用程序。
同样,对于流数据管道,对实时事件的订阅组合使得可以将Kafka用于非常低延迟的管道;但是,能够可靠地存储数据的能力使其能够在必须保证数据交付的关键数据中使用它或者与只定期装载数据的脱机系统集成,或者可能在较长时间内进行维护,流处理工具使数据在到达时进行转换成为可能。
有关Kafka提供的保证、api和功能的更多信息,请参见其余文档。