Apache Kafka 是一个分布式流式平台。
流平台有三个关键的能力:
- 发布和订阅记录流,类似于消息队列或企业消息传递系统。
- 使用容错耐用的方式存储记录流。
- 记录产生时处理数据。
Kafka 主要是用在两类应用中:
- 在收数端和实时计算或批处理计算框架之间做数据通道
- 作为处理流式数据的应用
为了解 kafka 怎么处理这些事情,需要先了解一下概念:
- Kafka 是运行在一台或者多台服务器的集群上的,并且可以扩展到多个数据中心;
- Kafka 集群以叫做 topics 的类别存储流记录;
- 每个记录都由 key,value,timestamp 组成;
Kafka 有 4 个核心 API:
- Producer API : 应用程序发布流记录到一个或者多个 Kafka topics;
- Consumer API : 应用程序订阅一个或者多个 topics 并且处理产生的数据;
- Streams API : 应用程序扮演着流处理器的角色,从一个或者多个输入流中消费数据并且将产生的数据输出到一个或者多个 topic 中。
- Connector API:构建并且运行将 Kafka topic 连接到现有应用程序或数据系统的可重用生产者或消费者。例如,关系数据库的连接器可能捕获对表的每个更改。
在 Kafka 中,客户端和服务器之间的通信是通过简单,高性能,语言无关的 TCP 协议完成的。此协议已版本化并保持与旧版本的向后兼容性。我们为 Kafka 提供 Java 客户端,但客户端有多种语言版本。
Topics and Logs
首先我们先了解一下 kafka 记录流提供的核心抽象 -topic。
Topic 是发布记录的类别或订阅源名称。kafka 中 topic 可以有多个订阅者,即可以有 0 个,1 个或者多个消费者订阅。
对于每一个 topic,Kafka 的集群都会维护着如下图这样的分区 log:
每个分区都是有序的、不可更高的记录序列,这些记录都是持续的被追加到结构化的 commit log 中。分区中的每个记录都会打上本分区唯一的被称为 offset 的序列 id。
Kafka 集群负责将所有发布的记录(无论是否消费)持久化,这些记录都是有一个配置的保留时间。例如,如果保留时间设置的是两天,那么记录发布之后两天内是可以消费的,两天之后就会被清除来事放空间。Kafka 的性能在数据大小方面实际上是恒定的,因此长时间存储数据不是问题。
实际上,每个消费者保留的唯一元数据是该消费者在 log 中的偏移量或位置。偏移量是由消费者来控制的:通常消费者在读取记录的时候会线性的推进其偏移量,但是实际上,既然消费者控制着消费的位置,就可以以任意顺序来消费记录。例如消费者可以重新设置偏移量来重新处理消费过的数据或者跳过最近的记录从头最新位置开始消费。
这些特性的结合就意味着 Kafka 消费者是非常 cheap 的,消费者的来去对于集群或者其他消费者没有太多影响。例如,可以使用命令行工具来查看任何一个 topic 中的内容而不会对其他消费者消费的内容产生影响。
日志中的分区有多种用途。首先,它们允许日志扩展到超出单个服务器的大小。每个单独的分区必须有托管它的服务器,但 topic 可能有许多分区,因此它可以处理任意数量的数据。其次,最重要的一点是它们充当了并行性的单位。
Distribution(分布式)
log 分区分布在 kafka 集群的服务器上,每个服务器处理数据并请求分区的共享。每个分区在服务器上都有配置的副本数量来容错。
每个分区都有一个服务器扮演者 leader 的角色,0 个或者多个服务器扮演者 followers 的角色。leader 处理这个分区的所有读写请求,follower 被动的复制 leader 的数据。如果 leader 崩溃了,其中的 follwers 就会自动变成新的 leader。每个服务器都充当其某些分区的领导者和其他服务器的追随者,因此负载在群集中得到很好的平衡。
异地数据同步
Kafka MirrorMaker 为集群提供了异地数据同步工具。使用 MirrorMaker, 消息可以跨多个数据中心或者云进行复制同步。也可以使用主 / 被的方式来备份或者恢复数据;或者主 / 主的方式来是数据更加靠近用户。
Producers(生产者)
生产者发布选择的数据到 topic 中。消费者负责选择记录分配到 topic 的那个分区中。可以使用 round-robin 方式简单的进行负载均衡或者使用语义分区函数来做。
Consumers(消费者)
消费者通过 consumer group name 来标记,每条记录都会发布到 topic 中后会被投递到订阅使用者组中一个消费者实例。消费实例可以在单独的进程中或者在单独的机器上。
如果所有消费者都有同样的消费组,那么记录将有效地在消费者实例上进行负载平衡;
如果所有的消费者有不同的消费组,那么每条记录都会广播到所有的消费者实例上;
有四个分区(P0-P3)的 Kafka 集群,集群有两台服务器,有两个消费组。消费组 A 有两个消费者实例,消费组 B 有 4 个消费实例。
然而,更常见的是,我们发现主题具有少量的消费者群体,每个“逻辑订阅者”一个。每个组由许多用于可伸缩性和容错的消费者实例组成。这只不过是发布 – 订阅语义,其中订阅者是消费者群集而不是单个进程。
Kafka 实现的消费方式是在消费者实例上划分日志中的分区以便于每个实例在任何时间点都是每个分区公平的独占消费者。维护组中成员资格的过程由 Kafka 协议动态处理。如果新实例加入该组,他们将从该组的其他成员接管一些分区; 如果实例死亡,其分区将分发给其余实例。
Kafka 只能保证每个分区的记录数是有序的,不同分区之间不保证有序。每个有序的分区加上使用 key 分区的能力对于大部分应用是足够的。然而,你需要总体有序的话可以使用一个分区的 topic。这就意味着每个消费组只有一个消费进程。
多租户
可以将 Kafka 部署为多租户解决方案。多租户通过配置哪些 topic 可以生产或者消费数据来启用。也可以选择是否支持配额。管管理员可以定义和强制执行配额,以控制客户端使用的代理资源。
Guarantees(高可用)
高级别的 Kafka 提供了如下的保证:
- 由生产者生产的消息发送到指定分区并且消息顺序是发送的顺序。这就意味着如果同一个生产者发送了两条记录 M1,M2,M1 先发送,那么在 log 中 M1 就会有一个比 M2 更小的偏移量。
- 一个消费者看见记录的顺序是在 log 中存储的顺序;
- 对于一个有 N 个副本的 topic,可以允许有 N - 1 个服务器崩溃而不丢失任何数据。
Kafka 作为一个消息系统
Kafka 的流概念与传统企业消息系统相比如何?
传统消息系统有两种模式:队列和发布订阅。在队列模式中,许多消费者从服务读取数据,每条记录都流向消费者之一;在发布订阅模式下数据会广播到所有消费者那里。这两种模式都有好处和坏处。队列的好处是可以把处理数据的压力分散到多个消费者实例上,这就可以扩展处理能力。然而队列不是多订阅的,一旦数据被读取了数据就没。发布订阅可以将数据广播到所有的消费者那里,缺点就是没有办法扩展处理能力由于消息会广播到每个订阅者那里。
Kafka 消费组的概念综合了这两个概念。消费组作为队列来看是可以把处理能力分散到多个进程上,作为发布订阅,可以将数据广播到多个消费组。
Kafka 相比于传统的消息系统的另一个优势是有更好的顺序保证。
传统队列在服务器上按照顺序保存记录,如果消费者从队列中消费数据的时候,服务器会按照存储的顺序提供记录。虽然服务器按照顺序分发记录,但是记录是异步的传递到消费者那里的,因此可能到达消费者的时候是乱序的。这就意味着在并行处理的消费上记录是乱序的。消息传递系统通常通过具有“独占消费者”的概念来解决这个问题,该概念只允许一个进程从队列中消耗,但这当然意味着处理中没有并行性。
Kafka 在这方面做的会更好。通过在 topic 中提出了一个 parallelism—the partition—within 的概念,Kafka 能够在消费者流程池中提供订购保证和负载平衡。这是通过将 topic 中的分区分配给使用者组中的使用者来实现的,以便每个分区仅由该组中的一个使用者使用。通过这样做,我们确保使用者是该分区的唯一读者并按顺序使用数据。由于有许多分区,这仍然可以让许多消费者实例的实现负载均衡。但请注意,消费者组中的消费者实例不能超过分区。
Kafka 作为存储系统
任何允许发布与消费消息分离的消息队列实际上充当了正在进行的消息的存储系统。Kafka 的不同之处在于它是一个非常好的存储系统。
写入 Kafka 的数据都写入磁盘并且都有备份用来做容错。Kafka 允许生产者等待确认消息以便于副本写入的完整性来确保数据仍然存在及时写入服务器宕机。
Kafka 很好的使用了磁盘的接口 — 在服务器上不论是写入 50KB 还是 50TB 的数据,效率都是一样的。
由于是存储系统,允许客户端控制其读取位置,可以将 Kafka 视为一种专用于高性能,低延迟提交日志存储,复制和传播的专用分布式文件系统
Kafka 作为流处理系统
只有读,写,以及存储流数据还是不够的,目的是实现流的实时处理。
Kafka 的流处理器是从输入 topic 的获取流数据,在输入中执行一些处理操作,最后将处理结果输出到 topic 中。
例如,零售应用可能会将销量以及出货量作为输入流,然后将重新排序以及价格调整以后的数据输出到 topic 中。
直接使用生产者或者消费者 API 就可以做一些简单的处理。然后对于复杂的变换处理,kafka 提供了提供了一整套集成的 Stream API。这就可以构建计算流的聚合或者流 join 这样不一般的应用。
这总便利帮助我们解决了这类应用面对的难题:处理乱序数据,代码变动需要重新处理,执行有状态的计算等等。
流 API 建立在 Kafka 提供的核心原语上:使用生产者消费者 API 作为输入,使用 Kafka 作为有状态的存储,在流处理实例间使用相同组的机制进行容错。
总结
将消息,存储以及流处理结合起来看似不是很常见,但是对于 Kafka 这种角色的流处理平台十分重要的。
像 HDFS 这样的分布式文件系统存储静态文件进行批处理。这样的系统允许存储和处理过去的历史数据。
传统的企业级消息系统可以处理订阅以后即将到来的消息,应用内建这个方式来处理即将到来的数据。
Kafka 将这些能力都融合进来,同时这种组合对于 Kafka 作为流媒体应用程序平台以及流数据管道的使用至关重要。
通过组合存储和低延迟订阅,流应用程序可以以相同的方式处理过去和未来的数据。也就是说,单个应用程序可以处理历史存储的数据,而不是在它到达最后一条记录时结束,它可以在未来数据到达时继续处理。这是包含批处理以及消息驱动应用程序的流处理的一般概念。
同样,对于流数据流水线,订阅实时事件的组合使得可以将 Kafka 用于极低延迟的流水线; 但是,能够可靠地存储数据使得可以将其用于必须保证数据传输的关键数据,或者与仅定期加载数据或可能长时间停机以进行维护的离线系统集成。流处理设施可以在数据到达时对其进行转换。
点击该链接,获取博客解锁验证码