Kafka基础
本篇文章基于官方文档,以一个小白的角度来初探Kafka。
背景
Kafka最早是由 LinkedIn 公司开发的,作为其自身业务消息处理的基础,后 LinkedIn 公司将 Kafka 捐赠给 Apache,现在已经成为 Apache 的一个顶级项目了。Kafka 作为一个高吞吐的分布式消息系统,目前已经被很多公司应用在实际的业务中了,并且与许多数据处理框架相结合,比如 Hadoop、Spark 等。
消息系统
在实际业务需求中,我们需要处理各种各样的消息,比如 Page View、日志、请求等,那么一个好的消息系统应该拥有哪些功能呢?
- 拥有消息发布和订阅的功能,类似于消息队列或者企业消息传送系统
- 能存储消息流,并具备容错性
- 能够实时地处理消息
以上3点是作为一个好的消息系统的最基本的能力。
消息系统的演变
最早的消息系统就是发布/订阅模式:消费者A与消费者B都订阅了消息源A和消息源B。这种模式简单,但有以下弊端:
- 消费者需要实时去处理消息,消息源和消费者都不会维护消息队列。如果消费者暂时没有能力消费,消息会丢失,也不能获得历史的消息
- 消息源需要维护订阅者(消费者)的信息,向多个消费者发送消息,还会处理消息反馈,这会让消息源变得越来越复杂
改进方案: 在消息源和消费者中间增加一个消息队列。消息源只需要将消息发送到消息队列中,其他交给消息队列去完成。
但如果有成百上千个消息队列,维护成本依然很高。可以演变出统一管理的消息队列集合:
- 能统一管理所有的消息队列,不需要开发者自己去维护
- 高效地存储消息
- 消费者能快速地找到想要消费的消息
这正是 Kafka 做的事。
Kafka 核心概念
- Kafka 是运行在一个集群上,所以它可以拥有一个或多个服务节点
- Kafka 集群将消息存储在特定的文件中,对外表现为 Topics
- 每条消息记录都包含一个 key、消息内容以及时间戳
Kafka 提供了四大核心接口:
| 接口 | 说明 |
|---|---|
| Producer API | 允许应用向 Kafka 中的 topics 发布消息 |
| Consumer API | 允许应用订阅 Kafka 中的 topics,并消费消息 |
| Streams API | 允许应用作为消息流的处理者,从 topicA 消费消息,处理结果发布到 topicB |
| Connector API | 提供 Kafka 与现有应用或系统的适配功能,比如与数据库连接器可以捕获表结构的变化 |
Topic 和 Partition
Topic 就像一个消息队列,生产者可以向其写入消息,消费者可以从中读取消息。一个 Topic 支持多个生产者或消费者同时订阅它,所以其扩展性很好。
Topic 由一个或多个 partition(分区) 组成:
- 每个 partition 中的消息是有序的,但相互之间的顺序不能保证
- 若需要所有消息都是有序的,最好只用一个分区
- partition 支持消息位移读取,消息位移由消费者自身管理
- 不同消费者对同一分区的消息读取互不干扰,消费者可以通过设置 offset 来控制想要获取的数据(从头读取、最新数据读取、重读等)
Distribution(分布式)
Kafka 是一个分布式的消息系统,配置多个 Kafka Server 节点后,就拥有分布式的能力,比如容错等。
- partition 会被分布在各个 Server 节点上
- 有一个 leader 处理所有的读写请求
- 其他 followers 会复制 leader 上的数据信息
- 一旦 leader 因故障无法提供服务,就会有一个 follower 被推举成为新的 leader
Geo-Replication(异地备份)
异地备份是主流分布式系统的基础功能,用于集群中数据的备份和恢复。Kafka 利用 MirrorMaker 来实现这个功能。
Producers
Producers 是消息的生产者,可以自己指定将消息发布到订阅 Topic 中的指定分区。策略可以自己指定,比如语义或结构类似的消息发布在同一分区,也可以由系统循环发布在每一个分区上。
Consumers(消费者组)
Consumers 是一群消费者的集合,称之为消费者组。它是更高层次的抽象,向 Topic 订阅消费消息的单位是消费者组(Consumer Group)。
消费者组的两条原则:
- 假如所有消费者都在同一个消费者组中,那么它们将协同消费订阅 Topic 的部分消息(根据分区与消费者的数量分配),保持负载平衡
- 假如所有消费者都在不同的消费者组中,并且订阅了同个 Topic,那么它们将可以消费 Topic 的所有消息
分配规则:
| 情况 | 分配结果 |
|---|---|
| 消费者数 < partition 数,且 = 1 | 消费所有消息 |
| 消费者数 < partition 数(N),partition 数为 M | 每个消费者能消费的分区数为 M/N 或 M/N+1 |
| 消费者数 = partition 数 | 每个消费者均等分配到一个分区的消息 |
| 消费者数 > partition 数 | 部分消费者得不到消息分区,出现空闲 |
Kafka 作为消息系统的优势
传统消息系统有两种模式:
消息队列模式:
- 表现形式:一组消费者从消息队列中获取消息,消息会被推送给组中的某一个消费者
- 优势:水平扩展,可以将消息数据分开处理
- 劣势:消息不是多用户的,消息被一个进程读取后便丢失
发布/订阅模式:
- 表现形式:消息会广播发送给所有消费者
- 优势:可以多进程共享消息
- 劣势:无法通过添加消费进程提高处理效率
Kafka 的改进: 通过 Topic 方式实现消息队列的功能,通过消费者组方式实现发布/订阅的功能,完美解决了两种模式的缺点。
Kafka 作为存储系统
Kafka 支持写入确认,保证消息写入的正确性和连续性,同时还会对写入磁盘的数据进行复制备份来实现容错。Kafka 对磁盘的使用结构是一致的,不管服务器磁盘存储的消息数据有多少,添加消息数据的效率是相同的。
Kafka 用于流处理
Kafka 提供了实时处理消息流的 Streams API。很多时候原始数据并不是我们想要的,我们想要的是经过处理后的数据结果。你可以利用 Streams API 来实现自己想要的功能,比如从输入 Topic 中获取数据,然后再发布到具体的输出 Topic 中。Kafka 的流处理可以解决处理无序数据、数据的复杂转换等问题。
消息在 Kafka 中的历程
- 消息生产者将消息发布到具体的 Topic,根据一定算法或者随机被分发到具体的分区中
- 根据实际需求,是否需要实现处理消息逻辑
- 若需要,则实现具体逻辑后将结果发布到输出 Topic
- 消费者根据需求订阅相关 Topic,并消费消息
总结
消息传递、存储、流处理这么功能单一来看确实很普通,但如何把它们完美地结合到一起,就是一种优雅的体现,Kafka 做到了这一点。
- 相比 HDFS 分布式文件存储系统:支持高效存储并且批处理数据,但只支持处理过去的历史数据
- 相比普通的消息系统:能处理现在至未来的数据,但没有存储历史的数据
Kafka 集众家之所长,使整个系统能兼顾各方面的需求。