Kafka核心功能: 高性能的消息发送和高效能的消息消费。
1. 快速入门
略...
2. 消息引擎系统
消息引擎系统就是我们常说的“消息队列”,只不过笔者认为消息引擎系统更加精致。
消息 是 消息引擎系统 最为关键的因素之一,如下图示:
设计消息引擎系统考虑了两个重要因素:
- 消息设计
- 传输协议设计
2.1 消息设计
Kafka的消息是用二进制方式来保存的,依然是结构化的消息。
2.2 传输协议设计
Kafka 自己设计了一套二进制的消息传输协议,而没有采用诸如 Google PB 这样的框架。在后面的章节中我们会详细阐述这其中的原因以及探讨 Kafka 消息传输协议及其背后的设计理念。
2.3 消息引擎泛型
最常见的两种消息引擎范型是 消息队列模型 和 发布/订阅模型。
消息队列模型 是 基于队列提供消息传输服务的,多用于进程间以及线程间通信。该模型定义了 消息队列、发送者和接收者。提供了一种点对点的消息传递方式,即发送者发送每条消息到队列的指定位置,接收者从指定的位置获取消息。每条消息被一个发送者发送出来,且只被一个消费者处理---发送者和消费者之间是一对一的关系。
发布/订阅模型 有主题(topic)的概念:一个 topic 可以理解为逻辑语义相近的消息的容器。
发布者将消息发送到指定的 topic 中,所有订阅了该 topic 的订阅者都可以接收到该 topic 下的所有消息。通常具有相同订阅的 topic 的所有订阅者将接收到同样的消息。
Kafka同时支持这两种消息引擎模型。
2.4 Java消息服务
Java消息服务,即 Java Message Service (简称 JMS) 。严格来说,只是一套API规范,提供了很多接口用于实现分布式系统间的消息传递。JMS 同时支持上面两种消息引擎模型。实际上,当前很多主流的消息引擎系统都完全支持JMS规范,比如 ActiveMQ、RabbitMQ 和 Kafka 等。 当然 Kafka 并没有完全遵守JMS规范,它另辟蹊径,探索出了一条独有的道路。
3. Kafka概要设计
Kafka 的设计初衷就是为了解决互联网公司超级大量数据的实时传输。为了实现这个目标,Kafka 在设计之初就需要考虑以下4个问题。
- 吞吐量/延时
- 消息持久化
- 负载均衡和故障转移
- 伸缩性
3.1 吞吐量/延时
通常来说 ,吞吐量是某种处理能力的最大值。对于 Kafka 而言,它的吞吐量就是每秒能够处理的消息数或者每秒能处理的字节数。
消息引擎系统还有一个 延时 的性能指标。它衡量的是一段时间间隔,可能是发出某个操作与接收到操作响应之间的时间。或者是在系统中导致某些物理变更的起始时刻与变更正式生效时刻之间的间隔。
显而易见,吞吐量越大越好,延迟越短越好。
在实际开发中,这两个指标通常是一对矛盾体(此消彼长)。
Kafka 是如何做到 高吞吐量 和 低延时 的?
得益于它对磁盘的使用方式的不同,Kafka 的写入操作是很快的。虽然 Kafka 会持久化数据磁盘,但本质上每次写入操作其实都只是把数据页 (page cache) 中,然后由操作系统自行决定什么时候把页面缓存中的数据写回磁盘上。这样的设计有 3 个主要优势:
- 操作系统页缓存是在内存中分配的,所有消息写入的速度非常快。
- Kafka 不必直接与底层的文件系统打交道。所有烦琐的I/O操作都交由操作系统来处理。
- Kafka 写入操作采用追加写入(append)的方式(磁盘顺序访问),避免磁盘随机写操作。
鉴于这一事实,Kafka 在设计时采用了追加写入消息的方式,即只能在日志文件的末尾追加写入新的消息,且不允许修改已写入的消息,因此它属于典型的磁盘顺序访问型操作,所以 Kafka 消息发送的吞吐量在实际使用过程中可以很轻松的做到每秒几万甚至几十万条消息。
那么消费端是如何做到 高吞吐量 、低延时 的。之前提到了 Kafka 是把消息写入操作系统的页缓存中的。那么同样的,Kafka 在读取消息时会首先尝试从 OS 的页缓存中读取,如果命中便把消息页缓存直接发送到网络的 Socket 上。这个过程就是大名鼎鼎的零拷贝(Zero Copy)技术。
除了零拷贝技术,Kafka 还大量使用页缓存。
总结:
Kafka ,就是依靠以下 4 点达到了高吞吐量、低延时 的设计目标。
- 大量使用操作系用页缓存,内存操作速度快且命中率高。
- Kafka 不直接参与物理I/O操作,而是交由最擅长此事的操作系统来完成。
- 采用追加写入方式,摒弃了缓慢的磁盘随机读/写操作。
- 使用以 sendfile 为代表的零拷贝技术加强网络间的传输效率。
3.2 消息持久化
Kafka 将消息持久化到磁盘上的好处如下:
- 解耦消息发送与消息消费:解耦,提升了整体的吞吐量。
- 实现了灵活的消息处理:消息持久化可以很方便的实现消息重演。
3.3 负载均衡和故障转移
Kafka 实现负载均衡实际上是通过智能化的分区领导者选举(partition leader election)来实现的。可以在集群的所有机器上以均等的机会分散各个 partition 的 leader ,从而整体上实现了负载均衡。
Kafka 服务器支持故障转移的方式是使用会话机制。每台 Kafka 服务器启动后会以会话的形式把自己注册到ZooKeeper 服务器上。一旦服务器运转出现问题,与 ZooKeeper 的会话便不能维持从而超时失效,此时 Kafka 集群会选举出另一台服务器来完全代替这台服务器提供服务。如下图:
3.4 伸缩性
每台 Kafka 服务器上的状态统一交由 ZooKeeper 保管,Kafka 只保存很轻量级的内部状态。扩展 Kafka 集群也只需要一步:启动新的 Kafka 服务器即可。 因此整个集群间维护状态一致性的代价是很低的。
4. Kafka 基本概念与术语
目前,Kafka 的标准定位是分布式流式处理平台。
其核心架构总结起来就三句话:
- 生产者发送消息给 Kafka 服务器(broker)
- 消费者从 Kafka 服务器(broker)读取消息
- Kafka 服务器(broker)依托 ZooKeeper 集群进行服务的协调管理
4.1 消息
Kafka 中的消息格式由很多字段组成,下图是 V1版本的完整消息格式。
消息由 消息头部、key 和 value 组成。 对于普通用户来说,掌握以下 3 个字段的含义就足够一般的使用了。
- Key : 消息键,对消息做 patition 时使用,即决定消息保存在某个 topic 下的哪个 partition。
- Value : 消息体,保存实际的消息数据。
- Timestamp : 消息发送时间戳,用于流式处理及其他依赖时间的处理语义。如果不指定则取当前时间。
4.2 topic 和 partition
topic : 主题,是一个逻辑概念,代表了一类消息。通常用 topic 来区分实际业务。
由于一个 topic 可能被多个消费者订阅。Kafka 没有采用 topic-message 的两级结构,而是采用了 topic-partition-message 三级结构来分散负载。 从本质来讲,每个 topic 都由若干个 partition 组成。
partition : 分区,没有太多业务含义,引入是为了提升系统吞吐量,因此在创建 topic 的时候可以根据集群实际配置具体的 partition 数,实现整体性能的最大化。
4.3 offset
Kafka 消费者端也由位移值(offset)的概念,但一定要注意这两个 offset 属于不同的概念。
4.4 replica
备份日志在 Kafka 中被称我为 replica(副本),它们存在的唯一目的就是防止数据丢失。
副本分为两类:领导者副本(leader replica)和(follower replica)。
4.5 leader 和 follower
Kafka 中的副本(replica)分为两个角色:leader 和 follower 。只有 leader 对外提供服务,follower 只是被动地追随 leader 的状态,保持与 leader 同步。follower 存在的唯一价值就是充当 leader 的候补。
Kafka 保证同一个 partition 的多个 replica 一定不会分配在同一台 broker 上,以实现备份冗余的效果。
4.6 ISR
ISR( in-sync replica), 与 leader 保持同步的 replica 集合。
Kafka partition 动态维护 replica 集合。该集合中的所有 replica 保存的消息日志都与 leader replica 保持同步状态。只有这个集合中的 replica 才能被选举为 leader ,也只有该集合中 所有 replica 都接收到了同 条消息, Kafka 才会将该消息置于“己提交”状态,即认为这条消 息发送成功。回到刚才的问题, Kafka 承诺只要这个集合中至少存在一个 replica ,那些“己提交”状态的消息就不会丢失一一记住这句话的两个关键点:①ISR 中至少存在一个“活着的” replica “己提 ”消息 。有些 Kafka 用户经常抱怨:我向 Kafka 发送消息失败,然后造成 数据丢失。其实这是混淆了 Kafka 的消息交付承诺( message delivery semantic) : Kafka 对于 没有提交成功的消息不做任何交付保证,它只保证在 ISR 存活的情况下“己提交”的消息不会 丢失。
正常情况下, partition 的所有 replica (含 leader replica )都应该与 leader replica 保持同 步,即所有 replica 都在 ISR 中。因为各种各样的原因,一小部分 replica 开始落后于 leader replica 的进度 。当滞后 一定程度时, Kafka 会将这些 replica “踢”出 ISR 。相反地,当这些 replica 重新 “追上” 了 leader 的进度时, 那么 Kafka 会将它们加回到 ISR 中。这一切都是自动维护的,不需要用户进行人工干预,因而在保证了消息交付语义的同时还简化了用户的操作成本。
5. Kafka 使用场景
5.1 消息传输
除了具备传统的消息总线的特征外,Kafka 还具有更好的高吞吐特性,其内置的分区机制和副本机制即实现了高性能的消息传输,同时还达到了高可靠性和高容错性。
5.2 网络行为日志追踪
Kafka 超强的吞吐量特性此时就有了用武之地。