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