🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
>[info]原文地址:https://kafka.apache.org/intro.html #### **介绍** Apache Kafka是一个分布式流平台【distributed streaming platform】。这到底是什么意思呢? 我们认为一个流平台有3个核心能力: 1. 它允许你发布和订阅数据流【streams of records】。在这个方面它类似于一个消息队列,或者像企业级消息系统。 2. 它允许你以一种可容错的方式来存储数据流。 3. 它允许你当数据出现时就能处理数据流。 Kafka在什么场景有优势? 它被用于2大类别的应用中: 1. 在系统或应用之间,构建实时流数据通道来可靠的获取数据。 2. 构建实时流应用,来传输数据流,或者对数据流做出响应。 为了了解Kafka是如何做到这些的,让我们深究下去,从底层探索Kafka的能力。 首先介绍一些概念: * Kafka作为集群运行在1个或多个服务器上。 * Kafka集群将数据流存储在被称为**主题**的类别中。 * 每一个数据记录都包含一个key,一个value,和一个timestamp。 Kafka有4个核心API: [生产者API](https://kafka.apache.org/documentation.html#producerapi):允许一个程序向一个或多个Kafka主题发布一个数据流。 [消费者API](https://kafka.apache.org/documentation.html#consumerapi):允许一个程序订阅一个或多个主题,并且能对产生过来的数据流进行处理。 [流API【Streams API】](https://kafka.apache.org/documentation/streams):允许一个程序扮演一个流处理器【stream processor】,它可以从一个或多个主题中消费一个输入流,并产生一个输出流通向一个或多个输出主题,有效的将输入流转化为输出流。 [连接器API【Connector API】](https://kafka.apache.org/documentation.html#connect):允许构建和运行可重用的发布者或者消费者,将Kafka主题连至已存在的应用中,或者数据系统中。比如说,一个和关系型数据库相连的连接器,就可以捕获一个表的每一次变化。 ![](https://box.kancloud.cn/9105e43104eb32b1b8549dca65e85aa8_1069x899.png) 在Kafka中,客户端和服务器之间的通信,使用的是一个简单的,高性能的,语言无关的[TCP协议](https://kafka.apache.org/protocol.html)。这个协议有版本控制,并且保持对老版本的向后兼容性。我们提供了Kafka的一个Java客户端,但是客户端其实可以支持[多种语言](https://cwiki.apache.org/confluence/display/KAFKA/Clients)。 #### **主题和日志** 我们首先深入了解一下Kafka关于数据流的核心概念 -- 主题【topic】 一个主题标识了一个被发布的数据类别。在Kafka中,主题总是有多个订阅者;也就是说,一个主题可以有0个,1个,或多个消费者订阅写入该主题中的数据。 对于每一个主题,Kafka集群都会维护一个分区日志,就像下面这样: ![](https://box.kancloud.cn/1a8465abb3bca8c36e8bab6f9eadf472_416x267.png) 每一个分区都是一个有序的,不可更改的记录序列。它被持续的追加,是一个结构化的日志。分区中的记录每一个都分配了一个按次序的id编号,被称作**偏移**【offset】,它唯一指定了分区内的每一条记录。 Kafka集群保存所有发布的数据 -- 无论它们是否被消费过 -- 使用了一个可配置的保存时长。例如,如果保存策略被设置为2天,那么在数据被发布2天内,它是可以被消费的,过了这2天,它就会被抛弃以释放空间。Kafka关于数据大小方面的性能实际上是恒定的,所以长时间存储数据并不是问题。 ![](https://box.kancloud.cn/3e24ce76224d4707dfb9ce453a92671e_2041x1243.png) 实际上,每一个消费者基本数据中仅保存元数据,即该消费者在日志中的偏移或者位置。这个偏移由消费者自己控制:按理来说,一个消费者在读取记录时,会线性前进它的偏移,但是,在实际情况中,由于位置是由消费者控制的,它可以以任何喜欢的顺序消费数据。比如一个消费者可以重置到旧的偏移来重新处理数据,或者跳过最近的一系列数据,直接从当前数据处开始消费。 这些特性组合起来,意味着Kafka的消费者是非常廉价的 -- 他们可以来去自如,而对集群或者其它消费者并没有太大影响。例如,你可以使用我们的命令行工具去读取任何主题的尾部内容。而不需要更改被其他消费者已经消费过的数据。 在日志中的分区服务于多个目的。首先,它们允许日志规模扩展到超出单台服务器的限制。而每一个单独分区必须适合托管它的服务器,但一个主题可以有很多个分区,所以它能够处理任意数量的数据。其次,它们扮演的是并行单元。【Second they act as the unit of parallelism—more on that in a bit.】 #### **分布式** 日志的各个分区,分布在Kafka集群的各个服务器上,每一个服务器处理数据,并请求一个共享的分区。为了实现容错,每个分区都会拥有冗余副本,副本的数量根据可容错的服务器数量,这个数字是可配置的。 每个分区都有一个服务器扮演“队长”【leader】,并且0个或多个服务器扮演“追随者”【followers】。队长处理所有针对分区的读取,和写入请求,而追随者则是被动的复制队长行为。如果队长宕机,其中一个追随者将会自动变为新的队长。每一个服务器都会为它托管的一些分区扮演队长,而为其它服务器上的分区扮演追随者,所以在集群中,负载被很好的均衡了。 #### **生产者** 生产者向他们选择的主题发布数据。它负责选择哪个记录指派给主题内的哪个分区。这个可以用轮询调度方式简单的实现负载平衡。或者也可以根据一些语义分区函数(例如基于记录中的一些key)来完成。马上将会看到更多关于分区的使用! #### **消费者** 消费者使用一个**消费者群组【consumer group】**名字给他们自己打标签,每一个发布到主题的记录,都会派发给每一个订阅消费者群组中的一个消费者实体。消费者实体可以在不同的进程中,或者在不同的机器上。 如果所有消费者实体有相同的消费者群组,那么记录将会在这些消费者实体中进行有效的负载均衡。 如果所有消费者实体有不同的消费者群组,那么每一个记录都会被广播给所有消费者进程。 ![](https://box.kancloud.cn/27ed316eb692a347dbcabacf09779d96_474x252.png) 一个2服务器的Kafka集群,托管4个分区(P0-P3)以及2个消费者群组,群组A有2个消费者实体,群组B有4个。 然而,更常见的,我们发现主题只有少量的消费者群组,每个“逻辑订阅者”一个群组。为了扩展性和容错,每个群组由多个消费者实体组成,这只是发布-订阅语义上的概念。其中订阅者指的是消费者集群,而不是一个单独进程。 在Kafka中实现消费的方式,是在消费者实体上划分日志中的分区,以实现在每一个时间点,每一个实体都是一个“公平共享”分区的独占者。在群组中维持成员的过程由Kafka协议动态处理。如果新的实体加入群组,他们将会从群组中其它成员手上接管一些分区;如果一个实体消亡,它的分区将会分发给剩余的实体。 Kafka只提供一个分区内记录的总顺序,而不是主题中的不同分区之间。每个分区排序结合上按key进行数据分区的能力对大多数程序是足够的。然而,如果你要求所有记录的总顺序,只有该主题仅有一个分区时才能满足要求,但这就意味着每一个消费群组只能有一个消费者进程。 #### **保证【Guarantees】** Kafka在高层上给出如下保证: * 生产者发送给一个特定主题分区的消息,将会按照它们发送的顺序进行追加。即:如果一个记录M1和一个记录M2都由同一个生产者发送,并且M1先发送,那么M1就会比M2拥有更小的偏移,并在日志中更早出现。 * 一个消费者实体浏览记录的顺序,就是记录被存储在日志中的顺序。 * 对于一个伴随复制因子为N的主题,我们可以最多容忍N-1个服务器宕机,不会丢失提交给日志的任何记录。 关于这些保证的更多细节,在文档的设计章节将会给出。 #### **Kafka作为一个消息系统** Kafka流式概念相比于一个传统的企业级消息系统如何呢? 传统的消息处理有2中模型:[ 队列](http://en.wikipedia.org/wiki/Message_queue) 和 [发布-订阅](http://en.wikipedia.org/wiki/Message_queue)。在队列模型中,一个消费者池可以从一个服务器读取,并且每一条记录都会送达至他们中的一个消费者;在发布-订阅模型中,记录被广播给所有的消费者。这2种模型都有利有弊。队列模型的优势是,允许你根据多个消费者实体划分数据处理,可以扩展你的处理进程。但不幸的是,队列不是多订阅者 -- 一旦一个处理进程读取了数据,那么这个数据就消失了。发布-订阅模型允许你向多个处理进程广播数据,但是无法扩展处理进程,因为每一个消息都会送达所有的订阅者。 在Kafka中消费者群组的概念囊括了这2种概念。作为一个队列,消费者群组允许你根据处理者集合(消费者群组中的成员)来划分处理。作为发布-订阅模型,Kafka允许你将消息广播给多个消费者群组。 >[success]自我理解:比如有6个消费者实体:A1,A2,A3,B1,B2,B3。其中A1,A2,A3消费者关注日志中的同一段数据P1段;B1,B2,B3关注日志中的另一段数据P2段。那么就可以将A1,B1打标签为G1群组,将A2,B2打标签为G2群组,将A3,B3打标签为G3群组;同时将日志分区为2个区【等于群组中成员的个数】。当P1区数据到来时,Kafka就会将P1数据广播给3个群组中的A1,A2,A3,这相当于传统的发布-订阅模型;并且它们3个消费者可以同时处理P1区数据,因为它们隶属不同群组,这就相当于传统的队列模型。 Kafka模型的优势是每一个主题都有这些属性 -- 它可以扩展处理,同时也支持多个订阅者 -- 就再没有必要选择这个或选择另一个。 相比于传统消息系统,Kafka还拥有更强的顺序保证。 一个传统的消息队列模型,在服务器上按顺序存储记录,如果多个消费者从这个队列中消费,服务器将会按照数据存储的顺序来交出它们。然而,即使服务器按顺序交出记录,这些数据也是异步的传输给消费者,所以它们送给不同消费者的顺序可能是不同的。这实际上就意味着记录的有序性在并行消费时丢失了。消息系统通常通过“独占消费者”的概念绕过这个问题,即允许仅一个进程在队列上进行消费,当然了这就意味着没有了并行处理。 Kafka做的更好。通过一个并行化的概念 -- 分区 -- 在主题内部,Kafka可以提供有序保证,以及在一个消费者进程池上的负载均衡。这是通过将主题中的分区指派给群组中的消费者达成的,以实现每一个分区仅被群组中的一个消费者消费。通过这种处理,我们确信消费者是那个分区唯一读者,并按序消费数据。因为有很多分区,所以即使在很多消费者实体的情况下依然能负载均衡。但是要注意,消费者群组中的实体数量不能多于分区的数量。 #### **Kafka作为一个存储系统** 任何一个允许发布消息和消费消息分离开的队列,实际上都扮演了正在传输的消息的存储系统。不同的是,Kafka是一个非常好的存储系统。 写入Kafka中的数据会被写入到硬盘中,并且复制副本实现容错。Kafka允许生产者等待确认,以便一个写入直到完全复制,并且保证即使服务器写入失败也能继续,才认为该次写入完成。