Kafka 简介
Kafka是分布式流平台。
一个流平台有3个主要特征:
- 发布和订阅消息流,这一点与传统的消息队列相似。
- 以容灾持久化方式的消息流存储。
- 在消息流发生时处理消息流。
Kafka通常使用在两大类应用中:
- 在系统或应用之间,构建实时、可靠的消息流管道。
- 构建实时流应用程序,用于转换或响应数据流
Kafka的几个基本概念:
- Kafka可以作为一个集群运行在跨越多个数据中心的多个服务上。
- Kafka集群按照分类存储的消息流叫做topic。
- 每一个消息由一个主键、一个值、和一个时间戳组成。
Kafka有4个核心的API:
- Producer API允许应用向一个或多个topic发送信息流。
- Consumer API允许应用订阅一个或多个topic并处理产生的信息流。
- Streams API允许应用扮演一个流处理器,从一个或多个topic消费输入流,并向一个或多个topic生产输出流。 实际上是转换输入流到输出流。
- Connector API构建和运行连接Kafka的可复用的生产者或消费者,到已存在的应用或数据系统。例如:连接一个关系型数据库捕获表中的每一次变化。
在Kafka中,客户端和服务器之间的通信是通过一种简单的,高性能的,语言不可知的TCP协议完成的。
Topics 和 Logs
我们了解一下Kafka为消息流提供的核心抽象——topic。 一个topic是一个消息发布时的分类。Kafka中的topic总是有0个、1个、或多个消费者订阅写入其中的数据。
对于每一个topic,Kafka集群保存着分区日志:
每一个partition是一个有序的不可改变的消息队列, 它可以持续的追加——结构化的提交日志。partitions中的每一个记录都会分配 一个有序的id,这个id叫做偏移量——每一个partition中的一个消息的唯一标识符。
Kafka集群通过配置保留时间持久化所有发布的消息,不管它是否被消费。例如:设置保留时间为2天,一个消息发布后的2天内,它可以被消费,超过2天,它将被丢弃以释放空间。
实际上,保存在每一个消费者基础上的唯一元数据是偏移量(offset)或者说是日志中消费者的位置。偏移量(offset)时候被消费者控制的: 正常情况下,一个消费者在读取数据时,线性增加它的偏移量,但实际上,消费者控制位置,它可以按照任何顺序处理和消费消息。例如:消费者可以重置一个老的偏移量,重新 处理过去的数据,也可以跳到最新的数据,从“现在时刻”起,消费数据。
这些特性意味着消费者是十分廉价的,他们可以来去自如,不会和集群中的其他消费者冲突。例如:你可以到任何topic的消息末尾,而不影响正在消费这个topic的其他消费者。
日志中的partitions服务着几个目的:首先,它们允许日志的大小超出适合单个服务器的大小。每一个单独的partition必须适合于自己的服务器。但是一个topic可以有许多个partition ,所以它可以处理任意数量规模的数据。其次它扮演着平行的单位。
分布式
日志的partitions分布在Kafka集群中的服务上,每一个服务处理partitions中的一份。每一个partition可以通过配置服务的数量进行复制,以达到容灾的目的。
每一个partition都有一个服务扮演着”leader”的角色,0个或多个服务扮演着”followers”的角色。”leader”处理partition所有的读写请求,”followers”通过”leader”进行数据备份。 如果”leader”失败了,”followers”中的一个会自动变成”leader”。
异地同步
Kafka的MirrorMaker为集群提供异地同步支持,使用MirrorMaker,消息可以跨越多个数据中心或云区域进行复制。你可以用主-被模式进行数复制和恢复,也可以用主-主模式 把数据置于离用户更新的地方。
生产者
生产者发布数据到他们选择的topic,生产者负责选择哪一个消息分配到topic中的哪一个partition。它可以通过轮询的方式简单的实现负载均衡,或者通过消息主键进行语义分区。
消费者
消费者用消费组名称标志着他们自己。发布到topic的每一个消息都会传送到每一个订阅的消费组中的一个消费实例上。消费实例可以按照进程分割,也可以按照机器分割。
如果所有的消费实例在一个消费组下,消息实际上是在消费实例上进行负载均衡。
如果所有的实例在不同的消费组下,每一个消息都会广播到每一个消费实例。
两个服务器Kafka集群托管四个分区(P0-P3)和两个消费者组。消费者组A有两个消费者实例,而组B有四个消费者实例。
通常情况下,我们发现topic都有一个小量的消费组,每一个“逻辑订阅者”都有一个。每一个消费组都由许多消费实例组成,为了扩展和容灾。 这仅仅在生产-订阅语义上,订阅者由一个消费集群代替了单一的进程。
Kafka消费的实现方式是通过消费实例分割日志中的partition,所以,在任何时间点,每一个实例都是partition合理份额中的专一消费者。 组内保持关系的进程被Kafka协议动态的处理。如果一个新的实例加入了组,它会从组内的其他成员分配一些partition。如果一个实例死掉了, partitions会分配到剩余的实例中。
Kafka仅提供partition内的消息排序,不是topic内不同partition之间的。按分区排序与按键分区数据的能力相结合,足以满足大多数应用程序的需求。可是, 如果你需要消息的整体排序,它可以用一个topic只有一个partition来完成,这意味着一个消费组中,只有一个消费实例处理数据。
多租户
你可以用多租户方案部署Kafka集群。多租户可以通过配置启用哪些topic可以生产或消费数据。还有配额操作的支持。管理员可以根据请求定义和执行配额以控制客户端使用的代理资源。
担保
作为高级别的Kafka,给出了一下的担保:
- 被发送到topic partition的消息会按照他们发送的顺序追加。如果M1被相同的生产者作为M2发送,M1先发送,M1有一个较低的offset,并且在日志中先与M2出现。
- 消费者按照日志中的顺序发现消息。
- 对于具有复制因子N的主题,我们将容忍多达N-1个服务器故障,而不会丢失任何提交给日志的记录。
Kafka作为消息系统
Kafka的流概念与传统企业消息系统如何比较?
传统的消息有连个模型:队列和发布-订阅。在队列中,每一个消息会分配到消费者中的一个,在发布-订阅模式下,每一个消息会广播到所有的消费者。 这两者中的每一个都有优点和缺点。队列的优点是可以通过多个消费者实例分割数据的处理,这可以扩展你的处理进程。不幸的是,队列不能有多个订阅者,一旦一个进程 读取了数据,它就消失了。发布-订阅允许你广播数据到多个进程,消息去了每一个消费者,你没有方式去扩展它。
Kafka消费组的概念整合了这两个概念。作为队列,消费组可以通过进程集合(消费组中的成员)分割处理。作为发布-订阅,Kafka允许你发布消息到所有的消费组。
Kafka模型的优点是每一个topic都有这两个属性,它可以扩展处理和有多个订阅者,不需要选择其中的一种。
Kafka比传统的消息系统有更强的排序保障。
传统的队列在服务端保存消息的顺序,如果多个消费者从队列中消费数据,服务按照存储的顺序分发消息。可是,虽然服务按照顺序分发数据,数据时异步的传递给消费者, 所以他们到达不同的消费者时是不能保证顺序的。这实际上意味着消息的顺序在平行消费面前是丢失的。消息系统为了解决这样的问题,通常有一个“专用消费者”的概念, 它只允许一个消费者从队列消费数据,这意味着没有平行处理。
Kafka可以更好的解决这个问题。通过有一个在topic内的平行partition的概念,Kafka既可以提供消息顺序的保障,又可以通过消费处理池进行负载均衡。 这是通过将topic中的partition分配给消费组中的消费者来实现的,以便每一个分区被组中的一个确定的消费者消费。通过这样做,我们确保了一个消费者 是partition的唯一读取者,并按照顺序消费数据。由于有多个partition,仍然可以通过多个消费者均衡负载。记住,组中消费者的数量不能大于partition的数量。
Kafka作为存储系统
任何允许发布消息并解耦消费的消息队列实际上都扮演着一个消息的存储系统。卡夫卡的不同之处在于它是一个非常好的存储系统。 写入Kafka的数据写入磁盘并进行复制以实现容错。Kafka允许生产者等待确认,以便写入在完全复制之前不会被认为是完成的,并且即使写入的服务器失败也能保证持续。 Kafka磁盘结构使用的规模很大 - 无论您在服务器上有50 KB还是50 TB的持久性数据,Kafka都会执行相同的操作。作为认真考虑存储并允许客户端控制其读取位置的结果,您可以将Kafka视为一种专用于高性能,低延迟提交日志存储,复制和传播的专用分布式文件系统。
Kafka作为流处理
仅读取,写入和存储数据流是不够的,目标是启用流的实时处理。
在Kafka中,流处理器是指从输入主题获取连续数据流,对该输入执行一些处理并生成连续数据流以输出主题的任何内容。
例如,零售应用程序可能会接受销售和装运的输入流,并输出一系列重新排序和对这些数据计算出的价格调整。
可以直接使用生产者API和消费者API进行简单的处理。然而,对于更复杂的转换,Kafka提供完全集成的Streams API。这允许构建应用程序进行非平凡的处理,从而计算聚合关闭流或将流连接在一起。
这个工具有助于解决这类应用程序面临的难题:处理无序数据,重新处理代码更改的输入,执行有状态的计算等。
流API基于Kafka提供的核心原语构建:它使用生产者API和消费者API输入,使用Kafka进行有状态存储,并在流处理器实例之间使用相同的组机制来实现容错。