本文翻译自:https://kafka.apache.org/intro,Kafka目前版本是1.0.0。转载请注明出处,谢谢。
欢迎拍砖指正。
前言
Kafka是一个分布式流处理平台,具备3个核心能力:
- 可以发布和订阅数据流,类似消息队列和企业消息系统。
- 可以容错的持久化数据流。
- 对数据流进行处理。
适合构建以下2种应用:
- 作为流数据管道在系统或应用之间实时可靠的获取数据。
- 作为流处理应用程序对数据流进行实时转换或响应。
明确一些概念
- Kafka以集群的方式在一台或多台服务器上面运行。
- Kafka集群以分类的方式存储数据流,一个分类就是一个topic
- 数据流里面的每条记录都包含一个key,一个value,一个timestamp(时间戳)。
Kafka的API主要分为4大类:
- Producer API:使应用可以发布数据流给Kafka topic(一个或多个)。
- Consumer API:使应用可以订阅topic(一个或多个),然后处理发送给这些topic的数据流。
- Streams API:使应用成为一个流处理器,消费从topic(一个或多个)接收到的输入流,然后产生输出流发送给其它topic(一个或多个)。
-
Connector API:用来构建可重用的生产者或消费者,连接已存在的应用或数据系统。例如:关系型数据库连接器可以捕获表的每一个变更。
Kafka内部客户端和服务端通讯使用简洁高效、语言无关的tcp协议。tcp协议版本化且向后兼容。Kafka提供了java和其它许多语言的客户端包。
Topics and Logs
一个topic是发布的数据的一种类型(名称),可以有多个(0,1,n个)订阅者。Kafka集群为每个topic维护着一个分区了的log(日志),如图:
partition是一个有序、不可变且不断提交到一个结构化日志的数据序列。每个数据都分配有唯一的叫做offset的有序ID,用于在partition中标识数据。 Kafka集群保留所有已发布的数据--无论它们是否已被消费(只要数据在配置的保存期内)。例如:假如设置2天的数据保存期,那么在数据发布后的2天内它都可以被消费,2天后数据会被丢弃以释放空间。Kafka性能随数据递增是常量级的,长时间保存数据没有问题。
实际上,每个消费者端持有的唯一元数据:其在日志中的 offset or position(偏移量或位置)。offset由消费者控制:一般情况下,消费者会往前移动offset来读取数据,实际上也可以移动到任意位置读取。例如,消费者可以把offset重置到之前某个位置来重新处理旧数据或跳过最近的记录直接消费最新的数据。
这些特性组合起来意味着Kafka的消费者可以非常便捷——他们可以在不影响集群或其他消费者的情况下加入或退出。例如,你可以使用我们的命令行工具来获取任意topic的最新数据,且不会影响现有消费者的消费。
topic的日志分割成partitions有几个目的。首先,它允许topic日志大小超出单台服务器的限制。虽然每个单独的partition大小不能超出承载它的服务器【真的超出了Kafka/我们如何处理?】,但是一个topic可能有多个partition,因此topic可以保存任意数量的数据。其次,partition作为并行的单元—more on that in a bit。
Distribution
log的partition分布在集群中的服务器上,每台服务器处理分布在其上的partition的数据和请求。为了容错,partition会在多台(具体数目可配置)服务器上进行复制备份。
partition会有一台服务器作为“leader”,其余的是“follower”(即主从)。leader处理所有的读写请求,follower被动的备份leader数据。 【被动,那谁通知它?leader?】 如果leader不可用,其中一个follower会自动成为新的leader。每台服务器对于分布其上的多个partition可能是leader也可能是follower,由此实现负载均衡。
Producers
Producers发布数据到他们选择的topics里面。producer负责选择将数据发送给topic中的哪个partition。可以采用循环的方式简单实现负载均衡,或者根据数据中的一些关键字来决定应该发送给哪个partition。
Consumers
消费者会给自己标记一个consumer group(消费者组)名称。【一个应用可以同时属于多个消费者组吗?--一个topic下不能?多个topic下可以吧?】发布到topic的数据会投递给订阅该topic的消费者组中的一个消费者实例。消费者实例可能在不同的进程中,也可能在不同的服务器上。
如果所有的消费者实例的组名称相同,数据将会有效地在消费者实例中负载均衡。
如果所有的消费者实例的组名称都不同,数据会广播给每个实例。
两台服务器组成的Kafka集群,托管着4个partition(p0-p3),有2个consumer group(消费者组)。A组有2个消费者实例,B组有4个。
通常,topic会有少量的消费者组(即逻辑订阅者)。每个消费者组则有许多消费者组成以伸缩和容错。这同样还是发布-订阅模式,只不过消费者是一个集群,而非单个进程。
Kafka中消费的实现方式是将log的partition划分给消费者实例,【如何划分?推荐这篇文章】(总的说来就是一个partition只会被同个消费者组中的其中一个实例消费,但一个消费者实例可以消费topic的多个partition)。Kafka协议可以动态的维护组内成员。如果有新的实例加入了消费者组,它会接管其它实例的部分partition。如果一个实例失效,它的partition会分配给其它实例。【算法是怎样的?可以自定义吗?】
Kafka只保证一个partition内的数据顺序,不保证不同partition间所有数据的顺序。【结合可以根据数据的key来分区的特性?】,可以满足大部分应用的需求。如果确实需要保证topic里面所有数据的顺序,可以设置topic只有1个partition,但这也意味着每个消费者组只能有1个实例同时去消费。
Guarantees
Kafka提供以下保证:
- 同一个producer发送给topic partition的消息会以发送的先后顺序写入到log中。也就是说,一个producer发送了消息M1、M2。M1先与M2发送,那么,M1会有一个较小的offset值,并且早于M2写入到log中。
- 一个消费者实例会按消息写入的顺序收到消息。
- 对于一个复制了n份的topic(应该是topic里面的partition复制n份)。即使其中n-1份失效,也可保证不丢失数据,前提是数据已提交到日志。【因为partition有多个备份,如何算提交成功?leader提交成功就算成功?还是所有follower也提交成功了才算?】
更多细节请参考设计部分的文档。
Kafka as a Messaging System
Kafka的流概念与传统的企业消息系统相比有何异同呢?
消息传递通常有两种模式:队列和发布订阅。队列模式时,服务器只会将数据发送给众多消费者中的一个;发布订阅模式时,数据会广播给所有消费者。这两种模式各有优缺点。队列的好处在于,它允许您将数据分配给多个消费者实例处理,这样可以让您对消息处理进行扩展。不过队列不支持多个订阅者——一旦消费者读取了某个数据,数据就消失了。发布订阅支持将数据广播给多个消费者,但消费者不能扩展,因为消息永远会发送给所有消费者。
Kafka中的consumer group抽象、统一了这个2个概念。与队列一样,consumer group允许您将消息的处理分配给多个进程(consumer group的成员)。同时也和发布订阅模式一样,Kafka允许您将消息广播给多个consumer group。
Kafka模型的优势在于:每个topic都允许横向扩展且支持多个订阅者——不用去选择队列模式还是消费订阅模式。
同时,与传统消息系统相比,Kafka提供更强的数据顺序保证。
传统队列在服务端记录消息顺序,如果多个用户从队列中消费,则服务器将按记录的顺序发送消息给消费者处理。但是,因为消息发送是异步的,消息到达不同消费者时的顺序未必还和服务端记录的顺序一致。这意味着在并行消费的情况下。记录的顺序丢失(无效了)。通常使用exclusive consumer(专有使用者)的概念来解决这个问题,它只允许一个进程消费队列,但也意味着无法并行消费消息。
Kafka作了改进,通过在topic中引入partition,Kafka可以同时满足消费者数据顺序保证和负载均衡的要求。这是通过将topic的partition分配给consumer group的消费者实例来实现的,这样每个partition就只会被组中的一个消费者实例消费(某个时间点上)。通过这种方法,我们确保该实例是该partition的唯一读取者,并按顺序消费数据。由于topic可以有许多partition,因此对众多消费者实例上来说仍然是负载均衡的。但是请注意,consumer group的消费者实例数目不能大于topic的partition数量(多出的消费者实例并不会消费数据)。
Kafka as a Storage System
任何允许发布消息、异步消费消息的消息队列对消息来说也是一个存储系统。Kafka是一个很好的存储系统。
Kafka会把接收到的数据写入硬盘,并且复制多份用于容错。Kafka允许producer等待,直到所有备份写入完成后才认为这次提交成功。
Kafka采用的磁盘数据结构具有很好的伸缩性:无论你需要存储的数据是50KB还是50TB,对Kafka来说都是一样的(高性能)。
由于认真对待存储并允许客户端控制它们的读取位置,您可以将Kafka视为一种专用于日志存储的高性能、低延迟、支持复制和分发的分布式文件系统。
更多细节请参考这个页面。
Kafka for Stream Processing
只是读、写和存储流数据还不够,其目的是实现实时流处理。
Kafka中的流处理器是指那些从input topic中读取输入流,处理后写出数据流到output topic的应用。
例如,一个零售应用会从销售和发货那里读取一个输入流,输出排序和价格计算后的数据流。
可以使用producer 和consumer api直接进行简单的处理。然而,为了处理更复杂的转换,Kafka提供了一个高度集成的Streams API,允许应用高效的从流中计算聚合或合并流。
这个特性有助于解决这类应用面临的问题:处理无序数据,代码修改后重新处理输入,执行状态计算等。
Streams API建立在Kafka提供的核心原语之上:使用producer和consumer处理输入,使用有状态存储,在流处理器上同样采用分组机制以便容错。
Putting the Pieces Together
把消息、存储和流处理结合在一起似乎不太常见,但对作为流处理平台的Kafka来说至关重要。
像HDFS 之类的分布式文件系统可以把静态文件存储起来用于批处理。这样可以有效处理历史数据。
传统的消息队列允许你订阅后处理将来收到的数据。以这种方式构建的应用处理未来到达的数据。
Kafka同时具备这2种能力,这对于使用Kafka的应用来说,无论是作为流处理应用还是流的管道都意义重大。
同时结合存储和低延迟订阅二者,流处理系统可以用同样的方式处理过去和将来的数据。当一个应用处理完历史数据后不必下线,可以继续处理接收到的将来时数据。 这是一个更广义的流处理,把批处理功能包含进了消息驱动的应用。
二者结合同样使Kafka用作为一个低延迟的实时流数据管道成为可能。可靠存储特性使Kafka可以用于关键数据传输或与那些需要定期停机维护的离线系统集成。流处理能力使得可以在数据抵达时进行转换。
更多特性的细节请参考文档.