第1章 初识Kafka
数据为企业的发展提供动力。我们从数据中获取信息,对它们进行分析处理,然后生成更多的数据。每个应用程序都会产生数据,包括日志消息、度量指标、用户活动记录、响应消息等。数据的点点滴滴都在暗示一些重要的事情,比如下一步行动的方向。
1.1 发布与订阅消息系统
先来了解发布与订阅消息系统的概念,并认识这个系统的重要性。
- 数据(消息)的发送者(发布者)不会直接把消息发送给接收者,这是发布与订阅消息系统的一个特点。
- 发布者以某种方式对消息进行分类,接收(订阅者)订阅它们,以便接收特定类型的消息。
- 发布与订阅系统一般会有一个broker,也就是发布消息的中心点。
1.1.1 如何开始
发布与订阅消息系统的大部分应用场景都是从一个简单的进程间通道开始的。例如,你的应用需要往别处发送度量指标(metric),可以在你的应用和另一个可以在仪表盘应用之间建立直接连接,然后通过这个连接推送度量指标,如下图所示。
但是这种方式在应对复杂场景时并不适合,如下图,如果场景变得复杂,直连会让节点间的通信变得一团糟
这时,技术债务开始凸显出来,于是你决定偿还掉一些。
- 你创建了一个独立的应用:用于接收来自其他应用程序的度量指标,并为其他应用提供一个查询服务。这样,之前架构的复杂度被降低到图1-3 所示的那样。
- 那么恭喜你,你已经创建了一个基于发布与订阅的消息系统。
1.1.2 独立的队列系统
在你跟度量指标打得不可开交的时候,你的一个同事也正在跟日志消息奋战。还有另一个同事正在跟踪网站用户的行为,为负责机器学习开发的同事提供信息,同时为管理团队生成报告。你和同事们使用相同的方式创建这些系统,解耦信息的发布者和订阅者。
图1-4所示的架构包含了3 个独立的 发布与订阅系统。
- 指标(Metric)的发布订阅系统
- 日志(logging)的发布订阅系统
- 用户跟踪(tracking)的发布订阅系统
此时,你真正需要的是一个单一的集中式系统,它可以用来发布和订阅 多种通用类型的数据,其规模可以随着公司业务的增长而增长。
1.2 Kafka登场
Kafka 就是为了解决上述问题而设计的一款基于发布与订阅的消息系统。
- 它一般被称为“分布式提交日志”或者“分布式流平台”。
- Kafka 中的数据是按照一定顺序 持久化保存的,可以按需读取。
- 并且Kafka 的数据 分布在Kafka集群的各个部分,具备数据故障保护和性能伸缩能力。
1.2.1 消息和批次(Message and batch)
Kafka 的数据单元被称为消息。
- 消息由字节数组组成。
- 消息可以有一个键,键也是一个字节数组。可以通过键,控制消息写入哪个分区。最简单的例子就是为键生成一个散列值,然后使用散列值对主题分区数进行取模,为消息选取分区。这样可以保证具有相同键的消息总是被写到相同的分区上。
为了提高效率,消息被分批次写入Kafka。
- 批次就是一组消息,这些消息属于同一个主题的同一分区。
- 如果每一个消息都单独穿行于网络,会导致大量的网络开销,把消息分成批次传输可以减少网络开销。
1.2.2 模式(schema)
对于Kafka 来说,消息不过是晦涩难懂的字节数组,所以有人建议用一些额外的结构来定义消息内容含义,让它们更易于理解,这就是模式。
Kafka 的开发者一般使用Apache Avro来定义模式
- 它最初是为Hadoop 开发的一款序列化框架。
- Avro 提供了一种紧凑的序列化格式,模式和消息体是分开的;
- 它还支持强类型和模式进化,其版本既向前兼容,也向后兼容。
1.2.3 主题和分区(Topic and Partition)
Kafka 的消息通过主题进行分类。
- 主题就好比数据库的表,或者文件系统里的文件夹。
- 主题可以被分为若干个分区,一个分区就是一个提交日志(commit log)。
- 消息以追加的方式写入分区,然后以先入先出(FIFO)的顺序读取。
- 要注意,由于一个主题一般包含几个分区,因此无法在整个主题范围内保证消息有序,但可以保证消息在单个分区内 有序。
- Kafka 通过分区来实现数据冗余(redundency)和伸缩性(scalability)。
- 分区可以分布在不同的服务器上,也就是说,一个主题可以横跨多个服务器,以此来提供比单个服务器更强大的性能。
图1-5 所示的主题有4个分区,消息被追加写入每个分区的尾部。
1.2.4 生产者和消费者
Kafka 系统的用户被分为两种:生产者和消费者
生产者创建消息。
- 一般情况下,一个消息会被发布到一个特定的主题上。
- 生产者在默认情况下把消息均衡地分布到主题的所有分区上,而并不关心特定消息会被写到哪个分区。
- 不过,在某些情况下,生产者也可以通过分区器和键,把消息直接写到指定的分区。
消费者读取消息。
- 消费者订阅一个或多个主题,并按照消息生成的顺序读取它们。
- 消费者通过检查消息的偏移量来区分已经读取过的消息。
- 偏移量是消息的一种元数据,它是一个不断递增的整数值,在创建每条消息时,Kafka 会把它附加到每条消息里。
- 消费者把每个分区最后读取的消息的偏移量 保存在Zookeeper 或Kafka 上,如果消费者关闭或重启,它的读取状态不会丢失。
可以将多个消费者组织成一个消费者群组,来共享一个主题:
- 同一群组的多个消费者共同读取一个主题。
- 群组保证每个分区只能被一个消费者使用,并且整个群组对于主题中的每条消息精确处理一次。
- 消费者与分区之间的映射通常被称为消费者对分区的所有权关系。
图1-6 所示的群组中,有3 个消费者同时读取一个主题。其中的两个消费者各自读取一个分区,另外一个消费者读取其他两个分区。
1.2.5 broker和集群
一个独立的Kafka 服务器被称为broker。
- broker 接收来自生产者的消息,为消息设置偏移量,并提交消息到磁盘保存。
- broker 为消费者提供服务,对读取分区的请求作出响应,返回已经提交到磁盘上的消息。
多个broker可以组成一个Kafka集群
- 每个集群都有一个broker 同时充当了集群控制器的角色。控制器负责管理集群,包括将分区分配给broker 和监控broker。
- 在集群中,每个分区都对应一个首领broker,该broker 被称为分区的首领。
- 并且一个分区可以分配给多个broker,但只有一个首领。其他broker都是这个首领的从者broker。这个时候会发生分区复制。首领broker会复制消息到从者broker。这种复制机制为分区提供了消息冗余
- 这样,如果首领broker 失效,从者broker可以接管领导权,而服务不会停止且消息不会丢失
保留消息(在一定期限内)是Kafka 的一个重要特性。Kafka broker 默认的消息保留策略是这样的:
- 要么保留一段时间(比如7 天),
- 要么保留到消息达到一定大小的字节数(比如1GB)。
1.2.6 多集群
kafka也支持多集群。如下图
多集群具体介绍略,后面章节会提到。
1.3 为什么选择Kafka
有很多其他的发布/订阅消息传递系统,例如:RabbitMQ、ActiveMQ等,那么是什么让Apache Kafka成为一个好的选择呢?
1.3.1 多个生产者
卡夫卡能够无缝地支持多个生产者,无论这些生产者使用的是多个主题还是同一主题。这使得该系统非常适合聚合来自许多前端系统的数据,并使其保持一致。
1.3.2 多个消费者
Kafka 也支持多个消费者从同一个消息流上读取数据,而且消费者之间互不影响。这与其他队列系统不同,其他队列系统的消息一旦被一个客户端读取,其他客户端就无法再读取它。
另外,多个消费者可以组成一个消费者群组,它们共享一个消息流,并保证整个群组对每条消息只处理一次。
1.3.3 基于磁盘的数据存储
Kafka允许消费者 非实时地读取消息,这要归功于Kafka 的消息保留特性。
消费者可能会因为自身处理能力低或突发的流量高峰导致无法及时读取消息
而持久化数据可以保证数据不会丢失。
- 消费者可以在进行应用程序维护时离线一小段时间,而无需担心消息丢失或堵塞在生产者端。
- 消费者可以被关闭,但消息会继续保留在Kafka 里。消费者可以从上次中断的地方继续处理消息。
1.3.4 伸缩性
Kafka可以通过增加broker的方式来实现横向扩展,它的伸缩性很灵活
1.3.5 高性能
上面提到的所有特性,让Kafka 成为了一个高性能的发布与订阅消息系统。通过横向扩展生产者、消费者和broker,Kafka 可以轻松处理巨大的消息流。在处理大量数据的同时,它还能保证亚秒级的消息延迟。
1.4 Kafka在数据生态系统中的重要性
在现代应用场景中,数据分布在很多不同的应用。这些应用可以整体上看作一个数据生态系统。
Kafka 为数据生态系统带来了循环系统,如图1-9 所示。
- 它负责各个应用间的消息传递,为所有应用提供一致的接口。
- 生产者应用和消费者应用之间不再有紧密的耦合,也不需要在它们之间建立任何类型的直连。
- 我们可以根据业务需要添加或移除应用,因为生产者不再关心谁在使用数据,也不关心有多少个消费者。
1.5 使用场景
1.5.1 活动跟踪(Activity tracking)
Kafka 最初的使用场景是跟踪用户的活动。
- 网站用户与前端应用程序发生交互,前端应用程序生成用户活动相关的消息。这些消息比如页面访问次数和点击量。
- 这些消息可以被发布到Kafka的一个或多个主题上,然后由一个或多个后端应用程序负责读取。这样,我们就可以用它们来生成报告,为机器学习系统提供数据,更新搜索结果等等。
1.5.2 发送通知(Messaging)
Kafka 的另一个基本用途是发送通知。
- 应用程序向用户发送通知(比如邮件)就是通过传递消息来实现的。
- 这些应用程序组件可以直接生成消息并发布到kafka,而不需要关心消息是如何发送给用户的,也不需要关心这些消息发送给用户时是什么格式的。
- 另一个公共应用程序会从Kakfa读取这些消息,并且处理消息成适合用户阅读的格式,然后发送给用户
1.5.3 度量指标和日志记录(Metric and logging)
Kafka 也可以用于收集应用程序和系统度量指标以及日志。
- 多个不同的应用程序定期把度量指标发布到Kafka 主题上,监控系统读取并分析这些消息。
- 日志消息也可以被发布到Kafka 主题上,然专门的日志搜索系统(比如Elasticsearch)或安全分析应用程序会读取并处理它们。
1.5.4 提交日志(Commit log)
Kafka 的基本概念来源于数据库的提交日志,所以使用Kafka 作为提交日志是件顺理成章的事。我们可以把数据库的更新放到Kafka 上,应用程序可以读取这些更新。
- 例如:数据库主库和从库之间的更新复制
- 或者合并多个数据库的更新到一个数据库实例上
1.5.5 流处理(Streaming processing)
用户可以编写小型应用程序来操作Kafka 消息,比如计算度量指标,为其他应用程序有效地处理消息分区,或者对来自多个数据源的消息进行转换。
1.6 起源故事
1.6.1LinkedIn的问题
LinkedIn 有一个指标监控系统。LinkedIn 还有一个比较复杂的请求跟踪系统。与此同时,还创建了另一个用于收集用户活动信息的系统。这些系统都需要从多个前端获取数据,这时就需要一个统一且通用的发布订阅系统。
1.6.2 Kafka的诞生
LinkedIn 的开发团队由Jay Kreps 领导。Jay Kreps 是LinkedIn 的首席工程师,之前负责分布式键值存储系统Voldemort 的开发。初建团队成员还包括Neha Narkhede,不久之后,Jun Rao 也加入了进来。他们一起着手创建一个消息系统,可以同时满足上述的两种需求,并且可以在未来进行横向扩展。他们的主要目标如下:
- 使用推送和拉取模型解耦生产者和消费者;
- 为消息传递系统中的消息提供数据持久化,以便支持多个消费者;
- 通过系统优化实现高吞吐量;
- 系统可以随着数据流的增长进行横向扩展。
最后我们看到的这个发布与订阅消息系统具有典型的消息系统接口,但从存储层来看,它更像是一个日志聚合系统。Kafka 使用Avro 作为消息序列化框架,每天高效地处理数十亿级别的度量指标和用户活动跟踪信息。LinkedIn 已经拥有超过万亿级别的消息使用量(截止到2015 年8 月),而且每天仍然需要处理超过千万亿字节的数据。
1.6.3 走向开源
略