英文原文
介绍
一个流处理平台有三个关键功能:
发布订阅记录流,和消息队列和企业消息系统一样;
以容错持久的方式存储记录流;
当流出现的时候进行处理;
Kafka一般用于两大类型的应用:
建立实时流数据管道,可靠地获取系统或应用之间的数据
建立实时流应用程序,对流数据进行转换,或者做出反应。
为了理解Kafka如何做这些是事情,我们来自下而上深入探讨下其功能。
首先,几个概念:
Kafka是所为一个集群在一个台或多台服务器上运行,集群可以跨多个数据中心。
Kafka集群以主题分类的方式存储记录流。
每个记录有一个Key,一个Value和时间戳组成。
Kafka有四类核心API:
Producer API,应用程序用来发布记录流到一个或多个Kafka主题。
Consumer API,应用程序用来订阅一个或多个主题并处理为其产生的数据流。
Streams API, 应用程序扮演流处理器,消费来自一个或多个主题的输入数据流,产生输出数据流到一个或多个主题。实际上就是把输入流转换成输出流。
Connector API, 创建和运行可重用的用于连接Kafka主题和已经存在的应用程序或数据系统的Producer 或 Consumer。 例如,一个连接到RDB的Connector可以捕获一个表的任何变化。
如下图所示:
Kafka的客户端和服务器使用简单、高效及语言无感知的TCP协议进行通信。TCP协议是版本控制的,并保持与老版本的向后兼容性。我们提供Java客户端。当然也有各种语言支持的客户端。
主题和日志
首先,让我们来探究一下Kafka为记录流提供的核心抽象--主题。
一个主题就是记录的一个类别或者输入(feed)名称。 在Kafka中,主题总是对应多个订阅者,也就是说一个主题可以有0个,1个或多个消费者订阅写入该主题的数据。
每个主题,Kafka集群维护一个如下的分区日志:
每个分区都是一个有序,不可变的额记录序列,一个持续增加结构化的commit日志。分区里的每条记录被分别一个序列ID号,叫作offset, 它在分区内是唯一的。
Kafka集群在一个可配置的保留期间内持久化所有发布的记录,不过它们有没有被消费。例如,假设保留期是2天,它发布后的两天内可以被消费,之后会被丢弃以腾出存储空间。Kafka的性能实际上受数据量的影响是常数,所以长时间存储数据不是问题。
事实上,基于每个消费者保存的唯一元数据就是offset或者是日志中的位置。offset由消费者控制。正常情况下,消费者依次读取记录依次前移offset。但事实上,由于offset是有消费者控制,消费者可以以它喜欢的任何顺序读取记录。比如,消费者可以重置offset到一个比较靠前的位置来重新处理数据,或往前跳到最新记录位置开始从“现在”的位置处理数据。
拥有这些特性意味着Kafka消费者可以方便地“来去自如“”,不用担心对集群或者其他消费者造成很大的影响。例如,你可以使用我们的命令行工具来“裁掉”任何主题的内容而不会改变任何消费者已消费的内容。
日志分区用于几个目的。首先,这允许日志可以超出适合单个服务器存储大小进行扩展。单个分区必须要适合它所在的服务器,但是一个主题可以有多个分区,所以它能处理任意数量的数据。其次分区是并行化处理的单位,更多的是这一点。
分布式
日志的分区分布在Kafka集群中的服务器上,每个服务器处理所有分区的一部分数据和请求。每个分区为容错跨可配置数量的服务器复制。
每个分区有一个扮演Leader的服务器和0个或者多个扮演Follower的服务器。Leader处理所有的针对该分区读写请求,Follower被动复制Leader的数据。假如Leader出现故障,其中一个Follower自动变成新的Leader。每个server作为它上面的一部分分区的Leader,同时是其他分区的Follower,所以集群内的负载被很好的均衡。
异地复制
Kafka MirrorMaker为你的集群r提供异地复制支持。使用MirrorMaker消息可以跨多这个数据中心或者云区域复制。你可以以此实现主动或被动场景下的数据备份和恢复。或者在主动或者被动的场景中将数据置于更接近用户的位置,或者支持数据本地化需求。
生产者
生产者发布数据到选择的主题。其负责为每个记录选择某个主题内的具体分区。这个可以通过简单的实现负载均衡循环策略实现。或者使用某些语气分区函数(比如根据记录中的某个关键字)来做。More on the use of partitioning in a second!
消费者
消费者使用消费者组名称为自己打标签。每条发布到一个主题的记录都会被发送到每个订阅的消费者组里的某个消费者实例。消费者实例可以是分开的进程,也可以位于分隔的机器上。
如果所有的消费者实例都属于相同的消费者组,那么记录实际上是被均匀的分发到这些实例上的。
如果所有的消费者实例属于不同的消费者组,那么每条记录都会向所有的消费者实例广播。
Kafka实现数据消费的方法是把日志分区分配到不同的消费者实例,以便每个实例在任何时间点上都是分区某个“合理份额”的独占消费者。一个组里头这种关系的维护过程是通过Kafka协议动态完成。如果有新的实例加入,它们会从从组里其他成员那里接管一些分区;如果一个实例终止,它负责的分区将被分配给组里其他的成员。
Kafka仅仅保证一个分区里的记录顺序,而不是一个主题所有不同分区之间的记录顺序。按分区排序加上按关键字对数据进行分区的能力足以满足大多数应用程序的需要。然而,如果你需要一个基于记录的整体顺序,可以为一个主题配置一个分区。不过这意味着每个消费者组仅有一个消费者处理器。
多租户
可以部署Kafka,通过配置哪些主题可以被生产或消费来支持多租户。也有支持配额的操作。管理员可以在请求上定义并执行配额来控制客户端使用的Broker资源。更多的信息,查阅安全文档。
保证
在一个比较高的层次,Kafka提供如下保证:
生产者发送到具体主题分区的的消息保存顺序与发送顺序一致。也就是说,假如M1和M2是用一个生产者发送的,而且M1先发送,那么M1比M2有更小的Offset,且会更早的出现在日志中。
消费者实例看到的记录都是以存储在日志中的顺序存放。
对复制因子为N的主题,我们将能保证最多N-1台服务器出现故障后提交到日志的记录仍然不会丢失。
更多的关于这些保证的详细信息这本文档的设计章节给出。
Kafka用作消息系统
与传统的企业消息系统相比,Kafka的流概念如何呢?
消息处理一般有两种模型:消息队列和发布-订阅。 对于消息队列,有一群消费者可以从一个服务器读取消息,每条消息会被发送到其中的某个消费者;发布订阅模式中,消息对所有消费者广播。这两种模式各有优缺点。消息队列的优点就是允许你把消息均匀的分配给多个消费者实例进行处理从而实现处理的伸缩。但缺点是,队列不支持多个订阅者--数据一旦被某个消费者读取,它就没有了。发布-订阅模式允许你向多个处理器广播数据,但无法对数据处理过程进行伸缩,因为每个消息都会被发送所有订阅者。
Kafka使用消费者组的概念来概括这两种模型。对于消息队列,消费者组允许你在一组处理器集合上分配处理任务。对于订阅--发布模式,Kafka允许你向多个消费者组广播消息。
Kafka的好处在于每个主题都拥有这两个特性,它既可以伸缩处理,也可以支持多个订阅者,不必只选择其中一种。
而且,与传统的消息系统相比,Kafka能更好的保证消息的顺序。
传统的消息队列在服务器端维护消息的顺序。当多个消费者从队列里读取消息时,服务器会以它保存消息的顺序分发消息。虽然如此,消息却是异步发送给消费者的,所以它们到达不同消费者的顺序可能会不同。这实际上意味着在并行处理的情况下消息的顺序会丢失。为了避免这种情况发生,消息系统经常使用专一消费者的概念,仅允许一个处理器来处理一个队列里面的消息。当然这也就不存在消息的平行处理了。
Kafka做得更好。通过主题内并行概念--分区,Kafka既能够确保消息的顺序性又能够在一组消费者处理例程上实现负载均衡。这是通过把一个主题内的一个分区分配给消费者组中的一个消费者来实现的,这样每个分区仅由组中的一个消费者使用。这样,我们就能确保一个消费者是一个分区的唯一读者,顺序读取数据。因为有多个分区,所以同时需要在多个消费者实例间均衡负载。不过需要注意的是,在一个消费者组中不能有比分区数更多的消费者。
Kafka用作存储系统
任何把发布消息和读取消息解耦的消息队列实际上是在一个实时消息存储系统。不同的是,Kafka是一个非常好的存储系统。
写入Kafka的数据会保存到磁盘并为了容错而进行复制。Kafka允许生产者等待确认数据是否进行了完全复制并确保持久化(即使写入服务器失败),否则可以认为写操作没有完成。
Kafka使用的磁盘结构具有良好的扩展性。无论你在服务器上存有50KB还是50TB的持久化数据,Kafka都将表现出一样的性能。
精心设计的存储加上允许客户读取位置的控制,Kafka可以被视为一种专用于高性能、低延迟的执行日志存储、复制和传播的特定目的分布式文件系统。
关于Kafka执行日志存储及复制的设计详细信息请查阅这里。
Kafka用作流处理
对数据流进行读、写、存储还不够,还需要进行实时数据流处理。
在Kafka中,流处理是指包括通过作为输入的主题获取持续的数据流,执行相应的处理,在作为输出的主题中产生相应的持续的数据流的所有内容。
例如,一个零售应用程序会把销售和装运作为输入流数据进行计算,然后把调整后的顺序和价格流输出。
一些简单的处理可以直接使用生产者和消费者 API 做。但是对于复杂的转换,Kafka提供了充分集成的流处理API。 使用这些API就可以构建一些应用做一些数据流的聚合计算或者把数据流连接在一起的重要的处理。
这帮助解决这类应用程序面对的难题:无序数据处理,当代码变更后数据的重新处理,执行有状态的计算等。
流处理API基于Kafka提供的核心原语构建。它使用生产者和消费者API输入,使用Kafka作为状态存储,并在流处理实例之间使用相同组的机制实现容错。
把那些都放在一块
把消息、存储和流处理结合在一起可能并不常见,但这对作为流处理平台的Kafka来说是至关重要的。
一个像HDFS一样的分布式文件系统可以存储用来进行批处理的静态文件。实际上这样的系统是处理来自过去的历史数据。
传统企业消息系统允许处理你订阅之后的未来的数据。基于这种系统构建的应用程序当未来的数据到达之后对数据进行处理。
Kafka结合了这两种功能。这对Kafka不管是作为流处理应用的平台还是用作流式数据管道都是很关键的。
结合存储和低延迟订阅,流处理应用程序可以统一对待过去和未来的数据。也就是说单个的应用程序能处理历史的已经存储的数据,但不会在读到最后一条记录后结束,新的数据到达时后会继续处理。这就是既包含批处理应用又包含消息驱动应用的通用流处理概念。
同样的,对于流式数据管道,Kafka结合订阅实时事件可以用作非常低延迟的数据管道;但Kafka可靠存储数据的能力使得它也适用于必须确保被分发的关键数据和与仅仅定期加载数据或者可能因延长的维护时间而宕机的离线系统集成。流处理设施使得数据在到达时进行转换处理。
关于Kafka提供的保证、API及功能,查阅这里。