Apache Kafka是一个分布式流平台。准确的说是什么意思呢?
我们认为流平台具有三种关键能力:
1、让你对数据流进行发布订阅。因此他很像一个消息队列和企业级消息系统。
2、让你以高容错的方式存储数据流。
3、让你在数据流出现时处理他们。
kafka擅长什么?
他通常被用在两大类应用:
1、构建一个能可靠的在系统或应用之间获取数据的实时的数据流管道。
2、构建一个能转换或响应数据流的实时流应用。
为了更好的理解kafka是如何做到上面的事,下面我们深入探索kafka的各种姿势。
首先了解几个概念:
1、kafka是以集群的方式运行在一台或多台服务器上
2、kafka集群数据存储在我们称作topic的分类里
3、每个记录由一个key,一个value和一个时间戳组成
kafka有下面四个核心APIs:
1、Producer API允许一个应用广播一个数据流到一个或多个kafka topic
2、Customer API允许一个应用订阅一个或多个topic,并且处理生产给他们的数据流
3、Stream API 允许应用像一个流处理器,消费从一个或多个topic输入的数据流,然后向一个或多个topic生产出输出数据流,在输入和输出之间高效的转换
4、Connector API 允许创建并运行可重复利用的连接kafka topics到现存的应用和数据系统的生产者和消费者。举个栗子,一个关系型数据库的connector可以捕获到表的每一个更改
在kafka中,客户端与服务端的通信通过一个简单的,高效的,与语言无关的TCP协议完成。这个协议的版本向后兼容老版本。我们为kafka提供了一个java的客户端,但是客户端可以使用很多语言。
Topic 和 Logs
让我们第一次深入了解kafka为数据流提供的核心抽象概念---the topic
topic是发布记录的类别或提要名称。topic在kafka中总是被多重订阅,因此,一个topic可以有0个,一个,或多个订阅了其写入数据的消费者。
对于每个topic,Kafka集群维护一个像这样的分区日志:
每个分区都是一个有序的,不可变的记录序列,这个序列会被不断的增加一种结构化的提交日志。在分区中的记录会被分配一个叫做offset(偏移量)的连续的id号,这个offset是每个在分区中的记录的唯一识别标示。
kafka集群保存所有的发布记录,无论他们是否被消费,使用一个可配置的保存时间。例如,保存策略设置为2天,那么在这个消息被发布后的两天内,他依然可以被消费,之后他会被丢弃来释放空间。kafka的是高效和稳定的,表现在他对数据大小的尊重,因此长时间存储不是问题。
实际上,在每个消费者基础上保留的唯一元数据是日志中该消费者的偏移量或位置。这个offset被消费者控制:通常一个消费者在读取记录时将会进行线性偏移,但是,实际上,因为位置是由消费者控制,只要他愿意,他可以任何循序消费记录。例如一个消费者可以重置到老的offset去重新处理来自以前的数据,或者往前跳到最近的记录并从“现在”开始消费。
这种多特征的组合意味着kafka消费者非常轻量-他们来去自如,不会对集群和其他消费者带来很大影响。例如,你可以使用我们的命令行工具去“tail”所有的topic内容而不会更改被现存消费者消费的记录。
日志中的分区有很多用途。一、他们允许日志量超过单个服务器所承受的大小。每个独立的分区必须受限于运行它的server,但是一个topic可以有多个分区,因此他可以处理任意大小数据。二、它们作为并行的单元。
Distribution
日志上的分区被分布在kafka集群的服务器上,每个服务器处理一部分分区的数据和请求。每个分区都跨可配置数量的多机器进行数据复制以保证容错。
每个分区都有一个被视为leader的服务器,0个或多个服务器作为followers。leader处理所有的读写请求,followers被动的复制leader。如果leader挂了,followers中的一个将自动的成为新的leader。每个服务器作为他自己部分分区的leader,同时也作为其他分区的follower,因此在集群中负载非常均衡。
Producers
生产者发布数据到指定的topic。生产者有责任选择记录分配到topic的哪个分区。这可以基于轮询(round-robin)来完成进行简单的负载均衡,或者根据语义分区函数(假如基于记录中的一些关键字)。
Consumers
消费者用一个消费者组名标记自己,每个记录发布到一个topic被传递到包含每个订阅消费组的消费者实例。
消费者实例可以在分开的进程中也可以在分开的机器。
如果所有的消费者实例有同样的消费组号,那么记录会很高效的在消费实例中负载均衡。
如果所有的消费者实例有不同的消费组号,那么每个记录将广播到所有消费者进程。
一个两台服务器的kafka集群运行四个分区(P0-P3)有两个消费者组。消费组A有两个消费实例,组B有四个。
我们发现这个topic有很小的消费组数量,每个消费组代表一个“逻辑订阅者”。每个组由许多消费者实例组成为了可扩展性与高容错性。这就是一个订阅者由一个消费集群代替了原来的单一进程的发布订阅结构。
在Kafka中实现消费的方式是将日志中的分区划分给消费者实例,以便每个实例都是在任何时间点上“公平共享”分区的唯一使用者。这个维护组员的进程由kafka协议动态处理。如果新实例加入组,他们将从其他组的成员接管一些分区;如果一个实例挂了,他的分区将会分给存在的实例。
需要注意的是,在consumer grup中的consumer instances的数量不能多于partitions的数量。
kafka仅提供在一个分区内的完全排序记录,不是在同一topic不同分区之间的。结合每个partition内部消息数据的有序性和按Key划分多partitions的能力,对大多数应用都够用了。
然而,如果你的应用需要的是一个基于全部消息数据的完全的有序队列,也可以通过一种特殊的方式来达到目的。即为一个topic只划分一个partition,且在consumer group中只能有一个consumer进程。
Guarantees
(1)一个生产者向指定topic的一个partition发送的消息,将会被按消息的发送顺序追加到队列中。因此对于一个生产者先后发送的消息M1和M2,M1会比M2有一个更小的 offset,并且会更早的出现在日志中。
(2)一个consumer instance看到的消息顺序与它们在Log中存储的顺序相同。
(3)对于一个复制因子为N的topic,可以容忍N-1 server宕机不丢失数据。