>[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允许生产者等待确认,以便一个写入直到完全复制,并且保证即使服务器写入失败也能继续,才认为该次写入完成。