Kafka

Published: by Creative Commons Licence

  • Tags:

安装

快速入门

安装

使用docker swarm启动,我使用的compose文件为https://github.com/wurstmeister/kafka-docker/blob/master/docker-compose-swarm.yml

介绍

Apache Kafka是一个分布式流式平台。一个流式平台有三个关键能力:

  • 发布和订阅数据流,类似于消息队列和企业消息系统。
  • 以容错持久化的方式存储记录流。
  • 当记录流出现时处理它们。

Kafka通常用于两大类应用:

  • 构建实时流式数据管道,可靠地在系统和应用之间传递数据。
  • 构建实时流式应用,转换或对数据流做出响应。

为了理解Kafka是如何做到这些事情的,让我们深入并由底至上探索Kafka。

首先一些概念:

  • Kafka可以跨多个数据中心并在一个或多个服务器上以集群的方式运行。
  • Kafka集群按照称为主题的分类存储记录流。
  • 每一条记录都包含一个关键字,一个值,一个时间戳。

Kafka有四个核心API:

  • 生产者API允许一个应用将一个记录流发送到一个或多个Kafka主题。
  • 消费者API允许一个应用订阅一个或多个Kafka主题,并处理提供给这些主题的记录流。
  • 流API允许一个应用表现地像一个流处理器,消费一个来自一个或多个主题的输入流,并输出一个流到一个或多个主题,快速地将输入流转换为输出流。
  • 连接器API,允许构建和执行可重用的生产者和消费者,并让他们将Kafka主题连接到现存的应用或数据系统中。比如,一个连接到关系型数据库的连接器可以捕获一张表的每一次改变。

在Kafka中,客户端和服务端之间的交流是通过一个简单,高性能,语言无关的TPC协议完成的。这个协议带有版本,并且以向后兼容的方式被维护。我们为Kafka提供了一个Java客户端,但是许多语言都有自己可用的客户端。

主题和日志

让我们首先深入了解Kafka为记录流提供的核心抽象——主题。

一个主题是一个分类,或者被发布记录的名字。在Kafka中的主题总是允许多重订阅的;即,一个主题可以有任意数目的消费者同时订阅写入其中的数据。

对于每个主题,Kafka集群维护了一个分片的日志,就如下图所示:

每一个分片都是一个有序的不可变的序列,其由连续追加的记录组成——一个结构化的提交日志。处于分片中的记录每一个都分配了一个序列id号,称为偏差(offset),并唯一地确定了该分片中的每一条记录。

Kafka集群持久化了所有被发布的记录——无论它们是否已经被消费过——使用一个配置好的存活周期。比如,存活策略设置为两天,那么一个记录发布两天内,它可以被消费,之后则会被删除以释放空间。Kafka的性能与数据的大小呈常数关系,因此长时间存储数据并不会成为问题。

实际上,每个消费者之上维护的唯一元数据是消费者所处日志的偏差(offset)。该偏差由消费者控制:通常消费者会在读取记录后线性增大offset,然而实际上由于offset是由消费者本地控制的,因此它可以以任何顺序读取记录。比如消费者可以通过重置offset,从而重复读取之前的记录,或者读取从现在消费者启动之后发送的记录。

这些特性的组合意味着Kafka的消费者非常廉价,它们可以独立运行,而不会对Kafka集群或其他消费者造成影响。比如,你可以使用我们的命令行工具来获取任何主题的尾部记录,但是不改变任何存活消费者所消费的记录。

对日志的分割是为了数个目的。首先,它允许日志的大小可以伸缩到一个系统可以容纳的程度。每一个日志分片必须适合所在系统,但是一个主题可以有多个分片,所以他可以处理任意数目的数据。其次,它们还扮演了并行的单位,这一点之后会细讲。

分布

日志分片分布在Kafka集群中的服务器中,每个服务器处理数据并请求共享的分片。每一个分片通过一个配置好的副本数维护日志副本,从而实现容错。

每一个分片都有一个服务器作为领导者(leader),而零个或多个服务器作为追随者(follower)。领导者处理分片所有的读写请求,而追随者则仅被动从领导者处复制数据。如果领导者挂了,他的一个追随者将自动变成领导者。每一个服务器都作为一部分自己管理分片的领导者,以及其它自己管理分片的追随者,所以负载很好地在集群中被均衡了。

GEO副本

Kafka MirrorMaker为你的集群提供了geo-副本支持。有了MirrorMaker,消息会跨越多个数据中心或云区域建立副本。你可以在主动/被动场景,放置数据到距离你的用户较近的地方,或者支持数据本地化需求。

生产者

生产者将消息发送给选中的主题。生产者负责选择记录该写入到主题的哪个日志分片中。可以通过循环取余的方式来简单地实现负载均衡,或者通过哈希的手法保证记录与分片之间的唯一对应关系。更多分区的手法很快就可以看到!

消费者

消费者为自己打上消费分组(consumer group)这一标签,每一条发布的记录会递送给所有订阅的分组,但是每个分组仅一个消费者会收到消息。消费者实例可以处于不同的进程或是处于不同的机器上。

如果所有消费者实例有着相同的消费分组,那么记录将会高效地在消费者之间实现负载均衡。

一个保护两个Kafka服务器的集群维护了四个分片,呗两个消费组所订阅。分组A有两个消费者实例,而分组B有四个消费者实例。

更通用地,然而,我们发现主题有少数几个消费者分组,每一个都是逻辑订阅者。每一个分组为了伸缩性和容错,由多个消费者实例组成。这里仅有发布订阅语义,而订阅者是一个消费组集群而非单独的进程。

在Kafka中消费的实现方法,是通过将日志分片瓜分给消费分组中的消费者,因此在任意时刻,在任何时刻,每一个实例,每一个实例都是分片的平等分享的互斥消费者。维护分组的成员关系是通过Kafka协议动态地处理的。如果新的成员加入到分组中,新成员会从其它成员接手一部分的分片;如果一个成员离开,它会将自己手头的分片分配给留存的实例。

Kafka仅提供了分片内的有序,而不支持跨分片。分片内有序结合可以通过关键字分配记录到特定的分片,对于大多数应用都是足够的。然而,如果你要求记录之间的全序,这可以通过仅为主题创建一个分片实现,虽然这会导致一个消费者分组中只能有一个消费者。

多租户

你可以将Kafka作为多租户解决方案。多租户机制通过配置哪些主题支持生产,哪些支持消费数据启用。也存在支持配额的操作。管理员可以定义和施行请求的配额,以控制客户端可以使用的中间件资源。

保证

高层次的Kafka提供了下面保证:

  • 一个生产者对一个日志分片发送的消息会按照发送的先后顺序出现在日志分片中。先发送的消息会比后发送的消息有着更小的偏移。
  • 一个消费者实例,会按照消息在日志中存储的顺序接收消息。
  • 对于一个副本数设置为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。这允许我们构建应用做一些非法的处理,比如计算流的聚合或者将多个流联结在一起。

这些功能帮助我们解决哪些应用面对的复杂问题:处理乱序数据,流转换,执行状态计算,等等。

构建于Kafka核心上的流API提供了:使用生产者和消费者API作为输入,使用Kafka作为状态存储,并使用同组机制在处理器实例之间进行容错。

放在一起

消息、存储和流处理的组合可能看起来不同寻常,但是他们都是Kafka扮演流平台这一角色的基础。

一个分布式文件系统,比如HDFS,允许为批处理存储静态字段。一个这样的系统,允许我们存储和处理历史数据。

一个传统的企业消息系统,允许消费者处理订阅后到来的消息。这种方式建立的应用处理未来到来的数据。

Kafka合并了这些能力,并且整合,对于Kafka作为流应用平台以及流数据管道非常重要。

通过组合存储和低延迟订阅,流应用可以以相同的方式处理过去和未来的数据。那是一个可以处理存储的历史数据的单独应用,但是它不会再最后一条数据处理完后结束,而是会继续处理未来抵达的数据。这是概括的流处理概念,就像消息驱动应用一样包括了批量处理的内容。

同样的,对于流数据管道,订阅到实时事件的的整合,使得它可能使用Kafka作为低延迟管道;但是存储可靠存储数据的能力使得它可以用于存储重要数据,或者与周期性地加载数据或会下线维护的离线系统整合。流处理功使得在数据到来时转换它变为可能。

代码实例

消费者和生产者