企业由数据所驱动的,我们获取信息,分析,处理,最后创建更多的输出。每个应用都在创造数据,无论是日志消息,统计消息,用户行为,传出的消息或者其他类型。每个字节都有它的作用,传入的数据会告诉接下来需要做什么。为了知道数据的意义,我们需要把数据从它产生的地方,传输到它能够被分析的地方。然后把分析的结果返回到它们能够被执行的地方。
处理数据的速度越快,我们的系统就能更敏捷,具有更快的响应。我们在传输数据上花费的精力越少,我们就能够集中处理核心业务。这就是为什么pipeline是数据驱动型企业的核心组件。我们如何移动数据几乎和数据本身的价值一样重要:

任何时候科学家们有分歧,那是因为我们没有足够的数据。我们可以在获取怎样的数据上产生统一的观点。我们获取了数据,然后数据解决了问题,要么你是对的,要么我是对的。然后我们就可以进行接下来的工作。
Neil deGrasse Tyson

Publish / Subscribe Messaging 发布/订阅消息

在讨论Apache Kafka的特性之前,需要了解一下publish-subscribe消息队列的概念和其重要性。发布-订阅消息队列的特征是消息的sender(publisher)并不直接将data(message)发送给receiver, publisher以某种方法对消息进行分类, 而receiver (subscriber) 会订阅接收特定类别的消息. Pub/Sub系统通常会有broker(中间人)来进行实现, 消息将被发送至broker.

How It Starts 由来

许多发布订阅使用案例都初始于相同的方式: 一个简单的消息队列或者一个进程间的通信Channel.比如,你编写的应用程序需要向某处发送监控指标(Metrics),你可以从应用程序直接连接到显示监控数据的dashboard,并通过该连接发送监控数据,如图1-1所示





Figure 1-1. 单个直接连接的监控指标系统


不久之后,你决定要分析长期的数据,但在dashboard中无法实现。于是你建立了一项新服务,可以接收监控指标,存储指标并进行分析。为了支持此功能,你可以修改应用程序以将指标写入这两个系统(实时监控和长期监控). 到目前为止,还有三个应用程序正在生成监控信息,并且它们都对这两个服务建立相同的连接。你的同事认为, 对服务器进行轮询以用于产生报警也是个不错的想法, 因此你在每个应用服务器添加根据请求提供监控指标的服务. 过一段时间后,你将会有更多应用程序需要使用这些服务器去获取各个指标并将其用于各种目的。现在的架构看起来可能如图1-2所示,连接变得更加复杂难以理清。





Figure 1-2. 多个使用直接连接的监控指标发布者


很明显到这里已经欠了很多技术债,你决定偿还一些回来。你使用一个应用程序接收来自所有应用程序的指标,并提供一个服务器来查询任何需要它们的系统的指标。这样就降低了架构的复杂性,类似于图1-3。恭喜,你已经构建了一个发布订阅消息系统!





Figure 1-3. 一个监控指标发布/订阅系统


Individual Queue Systems 单个队列系统

在你与监控这场战争中,你的一个同事也一直在对日志消息进行类似的工作.另一个则是在跟踪前端页面上的用户行为,并向正在从事机器学习的开发人员提供该信息,为管理层创建一些报告。您已经遵循了类似的方法来构建将信息的发布者与该信息的订阅者解耦的系统。 图1-4显示了这种结构,具有三个独立的发布/订阅系统。




Figure 1-4. 多个发布订阅系统


这肯定比之前点对点的连接好多了(图1-2所示),但是有很多重复工作。你的公司需要维护多个队列系统来存储数据,所有这些都有各自的bug和使用限制. 你肯定也知道未来将会有更多需要消息队列的情况. 你想要的是一个单一的集中式系统,允许发布通用类型的数据,并且能随着业务增长而增长.

Enter Kafka

Apache Kafka是一个设计用于解决以上问题的publish/subscribe消息系统. 通常被用于描述成 “分布式日志记录(distributed commit log)”. 文件系统或数据库日志记录被设计用于永久保存事务消息, 能够被用于重放系统到一个一致性的状态 (即每次重放都能到同一个状态). 类似的, kafka的数据也是持久化有序保存的, 能够被一致的读取(每次读取结果都相同). 另外, 数据能够被在系统中分布式的分发, 以提供失效备援, 同时也为横向扩展提供了可能性.

Messages and Batches 消息和分批发送

kafka中的数据单位也被称为message, 如果你以数据库的方式理解kafka的话, 你也许会以为message类似于数据库中的行或者记录. message在kafka看来仅仅不过是一系列的字节, 所以对于kafka来说message中的data并没有特别的格式, 也没有特别的意义. 当然message还是有一些元数据的, 代表message的key. Key同样也是字节序列, 同message一样, 对于kafka来说也没有特别的含义. Key被用于以一种可控的方式将message写入到分区(partitions)中. 最简单的模式就是将分区视为一个hash环, 保证相同的key会被写到相同的分区. Key的使用将在第三章中完整的讨论

考虑到效率, 消息一般以batch(batch)的方式写入到kafka中. 一个batch就是生产到同一个topic和partition的一系列消息的集合. 为单独一条消息而产生一趟网络请求会带来过多的网络负载, 而将消息集合到一起发送则可减少这种负载. 当然, 这是一个吞吐和延迟之间的权衡: 一批消息的量越大, 在单位时间内能够处理的消息量也就越大, 但是对于单个消息来说, 其传播需要的时间延迟就越大. 一般来说batch可以被压缩, 消耗更多的电能, 带来更加高效的数据传输和存储.

Schemas 模式

message对于kafka来说就是字节序列, 一般推荐给消息加上额外的结构, 这样消息能够被更好的处理. 有很多可供选择的消息schema, 取决于你的应用需求. 为了简化系统, JSON和XML都易用使用和人类阅读.但是他们都缺乏健壮的类型处理和不同模式版本的兼容性等特性. 许多Kafk开发者偏向于使用Avro框架, 最早被开发用于Hadoop的序列化框架. Avro提供了紧凑的序列化格式, 模式信息和消息负载是分开的, 当他们改变是, 并不需要生成代码, 同时具有强类型和版本前向和后向兼容.

对于Kafka来说, 一个一致的数据格式是很重要的, 能够让消费者和生产者进行解耦. 当他们耦合时, 订阅者需要进行更新以处理新的数据格式, 同时还要兼容旧的数据格式. 只有这样发布消息的应用才能使用新的格式. 这样希望使用消息的应用就与发布者耦合了. Schemas和Serialization的更多细节在第3章中有更加详细的描述.

Topics and Partitions Topic和分区

kafka中的消息被分类成topic, 与topic比较接近的类比是数据库中的table以及文件系统中的文件夹. Topic被划分为多个partitions.回到”commit log”的描述, 一个partition就是一个单独的log文件. 消息以append-only的方式写入到log文件中, 并且以从开始到结束的方式顺序读. 一般topic通常会有多个分区,消费在单个分区保证有序, 但是在多个分区间是不能保证的. 图1-5表示一个有4个分区的topic, 写操作附加在每个分区的末尾. 分区同样也是kafka提供冗余和扩展性的方式.每个分区都能放置在不同的server上,意味着一个topic能够水平扩展到多个server上,提供远超过一个server的处理能力.

Figure 1-5.一个拥有多个分区的topic

术语stream经常在比如kafka这样的系统时被使用. 一般而言, stream代表一个topic的数据, 不论这个topic有多少个partition. stream代表从生产者流向消费者的数据流. 这种描述数据的方式更常见于流数据处理, 比如Kafka流数据处理, Apache Samza和Storm等等实时处理框架. 这种处理方法能够与离线框架对比, 也就是hadoop, 被设计用于离线批量处理数据. 这个连接[Link to Come]提供了流处理的概述.
Kafka clients作为系统的使用者,有两种基本类型: producer和consumer. 生产者创建新消息。在其他发布/订阅系统中,这些可以被称为publisher/writer. 通常,生产者将向特定topic产生message。默认情况下,生产者不关心某个消息写入到什么分区,并且会均匀地分布topic的所有分区上的消息。在某些使用情况下,生产者会将消息定向到特定分区。这通常使用key和partitioner来完成,该partitioner会对key生成hash值,并将其映射到特定分区。这保证拥有相同key的消息将被写入相同的分区。生产者还可以使用定制的partitioner来根据业务逻辑将消息映射到分区。 第3章更详细地介绍了生产者。

consumer读取消息。在其他发布/订阅系统中,这些客户端可以被称为subscribers或readers。消费者订阅一个或多个主题,并按照它们生成的顺序读取消息。消费者通过跟踪消息的offset来跟踪它已经消费的消息。offset是元数据的一部分,一个不断增加的整数值,Kafka在每个消息产生时添加offset。给定分区中的每个消息都有唯一的偏移量。通过在Zookeeper或Kafka本身中存储每个分区的最后消费的消息的偏移,消费者可以停止并重新启动,而不会丢失它的最后消费位置。
consumer作为consumer group的一部分运行. 一个或多个consumer共同运行以消费一个topic. consumer group确保每个分区只会被这个consumer group中的一个consumer消费. 在图1-6中, 在这个consumer group中有3个普通consumer, 其中的两个consumer各自消费一个partition, 另外一个consumer独自消费两个partition. 我们一般将consumer和partition的映射称为consumer对partition的ownership(所有权).

这样, 消费者能够水平扩展以消费一个拥有大量消息的topic. 另外, 如果一个消费者挂掉的话, consumer group中其他消费者会自动平衡partition的消费分配以取代挂掉了的consumer. 在第4章中会对consumer和consumer group进行进一步的探讨.


图1-6 A consumer group reading from a topic

Brokers and Clusters

Kafka服务器也被称为broker. broker从producer中接收数据, 并且给其分配offset, 然后将其提交到磁盘中. 同样, broker也为consumer提供服务, 响应分区读取请求, 并返回已经提交到磁盘上的message. 随特定硬件的性能不同, 单个broker能够轻松的处理上千个分区和每秒百万条消息.

Kakfa broker被设计成作为kafka集群的一部分. 在borker集群中, 有一个会作为整个集群的controller(从当前存活broker中选取). 这个controller负责管理性的操作, 包括分配partition给broker和监控broker故障. 在集群中, 一个partition被一个broker所有, 这个broker被称为该分区的leader. 一个分区会被分配给多个broker, 这就是分区的副本集(Figure 1-7). 这对分区消息提供了冗余, 如果出现broker故障, 其他的broker就能接管leadership.

但是对一个分区的所有消费和生产操作, 都必须与leader连接. 集群操作, 包括分区复制具体在第6章中讲述.


Figure 1-7. Replication of partitions in a cluster

Apache Kafka的一个重要特性就是retention, 能持久化保存一段时间的message. Kafka broker为topic配置了默认保留期, 或者是一段时间(比如7天),或者是topic达到了一定的大小(比如1GB). 一旦达到了设定的限制值, message就过期了被删除, 所以retention配置是任何时间中, 最少的数据量. 每个topic也能有独自的配置, 所message只要还有用, 就可以一直存储. 例如, 用户追踪数据可能保留数天, 而应用监控数据可能只保存几个小时. topic可以被配置成log compacted, 这就意味着kafka可以只保存一个key的最新消息, 这对于只有最新更新才值得关注的changelog-type数据很有用.

Multiple Clusters

Why Kafka?

对于publish/subscribe消息系统, 有非常多的选择, 什么使得kafka成为一个好的选择?

Multiple Producers

Kafka能够无缝处理多个producer, 无论这些客户端使用了许多topic或者相同的topic.这使得kafka成为一个从多个前端系统聚合数据,然后提供一致的数据格式的理想系统. 例如, 一个通过多个微服务向用户提供内容的站点, 可以为统计page view而只设立一个topic, 所有的服务将page view以统一的格式写入这个topic. 消费程序能够以统一的数据格式来接收page view数据, 而不需要去协调多个生产者流.

Multiple Consumers

除了多个producer, kafka被设计成多个consumer来消费任何一个message stream, 而不相互干扰. 这和很多消息系统正好相反, 很多消息系统的消息一旦被其一个consumer消费, 就不能被其他消费者消费. 同时, 多个kafka consumer也可以选择作为一个group的一部分, 并且分享一个stream, 保证在一个组内, 消息只被消费一次.

Disk-based Retention(基于磁盘的消息保存)

kafka不仅仅能够处理多个消费者, 持久化的消息保存也意味着consumer并一定需要实时的处理数据. message被提交到磁盘, 并且根据持久化配置规则进行保存. 这个可以根据每个topic进行设置, 允许根据consumer的需求来设置不同的保留总量. 持久化保存意味着一旦消费者来不及消费或者突然出现流量高峰, 而不会丢失数据. 同样也意味着消息管理可以由consumer来负责, 比如消费消息掉线了一段时间,不需要担心消息会在producer上累积或者消息丢失, consumer能够从上次停止的地方继续消费.

Scalable

自从kafka项目开始, 就把灵活的扩展性设计进去了, 使其能够轻松处理任何数量的数据. 用户可以以1台主机来验证其理念, 然后将其扩展成3台broker组成的小型开发集群, 最后将其转成使用几十台的更大的生产集群, 随着数据量的增长达到甚至几百台的规模.扩展能够在集群不停机的情况下进行, 对于集群整体可用性没有影响. 这同样意味着, 拥有多个broker集群能够处理一定的broker故障, 而不影响为client提供服务. 集群如果要同时容忍更多的故障的话, 可以配置更高的replication factors. Replication在第6章中会详细探讨.

High Performance

所有的这些特性使得Apache Kafka成为一个能够在高负载下仍然有出色的性能的publish/subscribe消息系统. Producer, consumer和broker都能在大数据流的情况下轻松的扩展. 扩展过程能够在依然提供从生产到消费亚秒级服务的情况下完成.

The Data Ecosystem

许多的应用参与到我们所构建的处理数据的环境中. 我们定义了inputs–产生数据或者引入数据到系统的应用. 我们定义了输出–统计指标, 报告, 或者其他数据产品. 我们创造了loop, 一些组件从系统中读取数据, 进行一些处理, 然后将其回写到数据基础设施中, 以便后续其他地方处理. 无数的内容,大小,用途不同的数据通过这套流程进行处理.

如图1-9所示, Apache Kafka为这个数据生态系统提供了循环系统. Kafka传输来自各个基础设施的message, 为所有的client提供一致的接口. 因为引入了消息队列模式, producer和consumer就不再耦合, 之间也没有任何的直接连接. 组件能够随着业务的增加和融合而添加或移除, producer不需要关心谁在使用数据, 有多少consumer在使用数据.


Figure 1-9. A Big data ecosystem

Use Cases

Activity Tracking

Kafka最初的用途是用户行为追踪. 无论用户在前端进行什么交互操作都会产生message. 可以是passive的信息, 比如浏览页面和点击的追踪, 也可以是更加复杂的行为, 比如为用户资料添加信息. 这些message被发布到一个或多个topic, 随后被后端的消费者消费.以这种方式, 我们生成report, 为机器学习系统提供数据源, 更新搜索结果, 以及其他无数的应用.

Messaging

kafka的另外一个基本用途就是messaging. 也就是说, 应用把需要发送给用户的通知(邮件消息)发送到kafka. 这些组件可以生产message, 而不需要关注消息格式和消息将如何被发送. 一个通用的应用然后读取所有需要被发送的通知, 并且执行格式化, 选择如何发送它们. 通过使用一个通用组件, 不仅仅是减少了不同系统中的重复功能, 而且还能做一些有意思的转换功能, 比如聚集多条消息一次发送.

Metrics and Logging

Kafka同样是理想的处理应用和系统监控和日志的工具.可以用于多个生产者生产同一种类型的message. 应用定期发送它们的操作监控数据到kafka的topic中, 这些数据可以被用于系统监控和报警. 同样也可以被用于像hadoop这样的系统进行长周期的分析, 比如项目的年增长. Log message能够以同样的方式进行发送, 能够被路由至专门的日志分析系统,比如Elasticsearch或者安全分析应用. kafka提供了便利, 当目标系统需要改变时( 比我们要升级日志存储系统), 无需修改前端应用以及日志的聚合方式.

Commit Log

Kafka是基于commit log概念来构建的, 自然可以以这种方式来使用kafka. 数据库更改可以publish到kafka, 应用可以监控这个stream来接收实时更新. 这个changelog系统同样也可以用于复制数据库更新到远程系统. 或者合并多个应用的更新到单一的数据库视图. 持久化的保存也为changelog提供了buffer, 意味着如果消费应用失败的话, changelog可以进行重放. 另外,log compacted topic还能为log提供更长的保留时间, 如果对于每个key只保留最新一个更新的话.

Stream Processing

另外一个拥有多个类型应用的是流处理. 可以认为是提供了Hadoop的map/reduce处理类似的功能,但是Hadoop通常依赖于一个大的时间窗口的聚集数据, 几小时或几天, 然后以批处理的方式处理数据, 而流处理以实时的方式处理流. 流处理框架允许用户写一个小应用来对kafka message进行操作, 执行诸如计数, 对message进行分区让其他应用更有效的处理, 或者转换来自多个源的数据. Stream处理学习在 [Link to Come]中单独讲解.

The Origin Story

Kafka是为了解决LinkedIn的数据管道问题的需求而诞生的. 它被设计用于提供一个能够处理多种数据高性能的消息系统, 并且能够实时提供干净的, 结构化的用户活动数据和系统指标.

数据使我们所做的变得强大.
Jeff weiner, CEO of LinkedIn

LinkedIn’s Problem

在章节前面提到的, LinkedIn内部拥有一个使用自定义的收集器和一些开源工具的,收集系统和应用指标的, 存储和展示系统. 除了向传统的监控指标, 比如CPU利用率和应用性能等外, 还有一个复杂的使用监控系统的请求追踪功能, 能够查看一个用户的请求是如何在内部系统扩散的. 然而这个监控系统有很多故障. 这个监控系统主要使用轮询来获取消息, 监控数据间有很大的时间间隔, 并且也不能进行自动化服务. 这个系统需即使是最简单的任务也需要人工干预, 并且系统存在不一致, 不同系统中相同监控指标有不同的名字.

同时,还有一个用于收集用户活动踪迹信息的系统。这是一项HTTP服务,前端服务器将定期连接并发布一批消息(XML格式). 这些消息将被移动到离线处理,用于解析和整理。这个系统也有许多缺点。XML格式化不一致,并且解析XML的代价很昂贵。更改活动的类型需要在前端和离线处理间进行大量的协调工作. 即使这样,系统也会随着不断变化的模式而不断崩溃。用户跟踪建立在每小时批处理上,因此不能用于任何实时处理.

监控和用户追踪很难使用相同的后台服务, 监控服务太笨重了, 数据的格式并不适用于用户行为追踪, 并且其轮询模式也不适用于这种场景. 同时, 追踪服务对于监控来说又太脆弱了, 其批量处理的模式也并不适合实时监控和报警. 然而这些数据又有相同的特征, 并且信息之间的相关性(比如某种类型的用户行为会怎么影响系统性能)是非常期望得到的. 丢失某些特定类型的用户行为数据可能意味着相关的应用服务出现问题, 但是批量处理用户数据造成的数小时的延迟意味着对这些问题响应是很慢的.

在一开始, 我们研究了现有的开源解决方案以查找一个可以提供实时数据处理, 并且能够随着需要的处理消息的数量而扩展的方案. 原型系统是以ActiveMQ来搭建的, 但那时它还不能处理如此大的规模. 同时以LinkedIn的需求来使用,它又很脆弱, 很多bug会导致broker停止服务. 这将会积累客户端的连接, 并且影响到应用向用户提供服务的性能. 我们决定自己为data pipeline开发一个基础组件.

The Birth of Kafka

LinkedIn的开发组由Jay Kreps所领导, 之前是负责开发和开源发布分布式键值存储系统Voldemort的主程. 最开始的开发组还包括了Neha Narkhede,很快Jun Rao加入了. 他们一起决定创造一个能够满足(LinkedIn前面描述的)系统和未来扩展需求的消息系统.最初的目标包括:
- 通过push-pull模型来解耦producer和consumer
- 在消息系统内部提供消息持久化, 以满足多个consumer
- 为高吞吐量优化
- 能够随着数据的增长而水平扩展
设计的结果就是一个publish/subscribe系统, 有着典型消息系统的接口, 但是存储层像一个log aggregation(日志聚合)系统. 与采用Apache Avro做消息序列化结合, 这个系统能够有效的每天处理数十亿量级的系统监控数据和用户活动追踪数据. 随着时间的推进, LinkedIn对kafka的使用量超过了每天1W亿条消息(2015年8月),1PB的数据.

Open Source

kafka作为一个开源项目于2010年年末发布在github上. 开始受到开源社区的关注, 于2011年7月被提议和通过作为Apache软件基金会的孵化项目. 从那时起, kafka不仅继续得到LinkedIn的开发支持, 还聚集了一个由来自LinkedIn之外的contributors和committers组成的强大的社区. 因此kafka目前在许多组织里担当最大的data pipeline的角色. 在2014年秋季, Jay Kreps, Neha Narkhede和Jun Rao离开了LinkedIn成立了Confluent,用于为Kafka提供开发,商业支持和培训. 这两个公司, 同一直在增长的来自开源社区的贡献者们继续开发和维护Kafka, 使其成为大数据pipeline的第一选择.

The Name

关于Kafka的历史, 一个经常被问的问题就是Apache Kafka这个名字的由来, 以及他对这个应用的影响. 在这个问题上, Jay Kreps提供了以下的解答:

我认为Kafka是一个为写消息而特别优化的系统, 所以使用一个作家的名字会比较有意义. 我在大学时上过很多文学课, 并且比较喜欢Franz Kafka, 这个名字用在开源项目上听起来比较cool.
所以其实两者并没有太大的关系.
Jay Kreps

Getting Started With Kafka

现在你知道kafka是什么, 并且知道了使用kafka的背景知识, 接下来我们可以开始配置kafka,构建起你的data pipeline. 在下一章, 我们会探索kafka的安装和配置, 我们同样也会覆盖到如何选择适合kafka运行的硬件, 以及在生产上使用时需要注意的事情.