你好,Kafka

你们好,我是 kafka, 可能不少人都据说过我,知道我是 2011 年出生在 LinkedIn 的, 从那会儿到如今个人功能愈加强大了。做为一个优秀而又完整的平台,你能够在我上面冗余地存储巨大的数据量,我有一个具备高吞吐量 (数百万 / 秒) 的消息总线,你能够在这上面对通过个人数据进行实时流处理。 若是你认为我就只有上面的这些特色的话,那么你真的是太肤浅了。html

上面虽说的很好,可是并未触及到个人核心,这里我给你几个关键字:分布式,水平可扩展,容错,提交日志。java

上面这些抽象的词语,我会一一解释它们的含义,并告诉大家我是如何工做的。git

心里独白: 原本我是想要以第一人称来写这篇文章的,可是我发现我只能写出上面的了,再多的我就憋不出来了,因而我决定不要为难本身,仍是用用第三人称写吧 (写做的功底仍然须要锻炼)github

分布式

分布式系统由多个运行的计算机系统组成,全部这些计算机在一个集群中一块儿工做,对终端用户来说只是一个单一节点。 redis

分布式系统

kafka也是分布式的,由于它在不一样的节点(又被称为broker)上存储,接受以及发送消息,这样作的好处是具备很高的可扩展性和容错性。sql

水平可扩展性

在这以前,先看看什么是垂直可扩展,好比你有一个传统的数据库服务器,它开始过分负载,解决这个问题的办法就是给服务器加配置(cpu,内存,SSD),这就叫作垂直扩展。可是这种方式存在两个巨大的劣势数据库

  1. 硬件存在限制,不可能无限的添加机器配置
  2. 它须要停机时间,一般这是不少公司没法容忍的

水平可扩展就是经过添加更多的机器来解决一样的问题,添加新机器不须要停机,并且集群中也不会对机器的数量有任何的限制。问题在于并不是全部系统都支持水平可伸缩性,由于它们不是设计用于集群中(集群中工做更加复杂)。apache

容错性

非分布式系统中容易最致命的问题就是单点失败,若是你惟一的服务器挂掉了,那么我相信你会很崩溃。数组

而分布式系统的设计方式就是能够以配置的方式来允许失败。在5个节点的kafka集群中,你仍然能够继续工做即便其中两个节点挂掉了。 须要注意的是,容错与性能直接相关,你的系统容错程度越高,性能就越差。缓存

提交日志(commit log)

提交日志(也被称为预写日志或者事物日志)是仅支持附加的持久有序数据结构,你没法修改或者删除记录,它从左往右读而且保证日志的顺序。

commit log
是否是以为kafka的数据结构如此简单?

是的,从不少方面来说,这个数据结构就是kafka的核心。这个数据结构的记录是有序的,而有序的数据能够确保咱们的处理流程。这两个在分布式系统中都是及其重要的问题。

kafka实际上将全部消息存储到磁盘并在数据结构中对它们进行排序,以便利用顺序磁盘读取

  1. 读取和写入都是常量时间O(1)(当肯定了record id),与磁盘上其余结构的O(log N)操做相比是一个巨大的优点,由于每一个磁盘搜索都很耗时。
  2. 读取和写入不会相互影响,写不会锁住读,反之亦然。

这两点有着巨大的优点, 由于数据大小与性能彻底分离。不管你的服务器上有100KB仍是100TB的数据,Kafka都具备相同的性能

如何工做

应用程序(producer)发送消息(record)到kafka服务器(broker),这些消息会被其余应用程序(consumer)所处理,这些消息存储在主题(topic)中,而且消费者订阅该主题以接收新消息。是否是感受很像你平时写的代码——生产者消费者模式。

工做模式

随着主题变得很是大,它们会分红更小的分区(partition),以得到更好的性能和可伸缩性(好比存储了用户相互发送的消息,你能够根据用户名的第一个字母来进行拆分)。Kafka保证分区内的全部消息都按照它们的顺序排序,区分特定消息的方式是经过其偏移量(offset),你能够将其视为普通数组索引,即为分区中的每一个新消息递增的序列号。

分区

kafka遵照着愚蠢的broker和聪明的consumer的准则。这意味着kafka不会跟踪消费者读取了哪些记录并删除它们,而是会将它们存储必定的时间(好比1天,以log.retention开头的来决定日志保留时间),直到达到某个阈值。消费者本身轮询kafka的新消息而且告诉它本身想要读取哪些记录。这容许它们按照本身的意愿递增/递减它们所处的偏移量,从而可以重放和从新处理事件。

须要注意的是消费者是属于消费者组的,消费者组有一个或多个消费者。为了不两个进程读取一样的消息两次,每一个partition只能被一个消费者组中的一个消费者访问。

Kafka消费数据

持久化到硬盘

正如我以前提到的,kafka其实是将全部记录存储到硬盘而不在RAM中保存任何内容。你想知道这个如何作出这个选择的,其实这背后有不少优化使得这个方案可行。

  1. kafka有一个将消息分组的协议,这容许网络请求将消息组合在一块儿并减小网络开销,服务器反过来一次性保留大量消息,消费者一次获取大量线性块。
  2. 磁盘上线性读写很是快,现代磁盘很是慢的概念是因为大量磁盘寻址,可是在大量的线性操做中不是问题。
  3. 操做系统对线性操做进行了大量优化,经过预读(预取大块屡次)和后写(将小型逻辑写入组成大型物理写入)技术。
  4. 操做系统将磁盘文件缓存在空闲RAM中。这称为pagecache,而kafka的读写都大量使用了pagecahce
    1. 写消息的时候消息先从java到page cache,而后异步线程刷盘,消息从page cache刷入磁盘
    2. 读消息的时候先从page cache找,有就直接转入socket,没有就先从磁盘load到page cache,而后直接从socket发出去
  5. 因为Kafka在整个流程(producer - >broker - >consumer)中以未经修改的标准化二进制格式存储消息,所以它可使用零拷贝优化。那时操做系统将数据从pagecache直接复制到socket,有效地彻底绕过了Kafka broker。

全部这些优化都使Kafka可以以接近网络的速度传递消息。

数据分发和复制

咱们来谈谈Kafka如何实现容错以及它如何在节点之间分配数据。

为了使得一个borker挂掉的时候,数据还能得以保留,分区(partition)数据在多个broker中复制。

在任什么时候候,一个broker拥有一个partition,应用程序读取/写入都要经过这个节点,这个节点叫作----partition leader。它将收到的数据复制到N个其余broker,这些接收数据的broker叫作follower,follower也存储数据,一旦leader节点死掉的时候,它们就准备竞争上岗成为leader。

这能够保证你成功发布的消息不会丢失,经过选择更改复制因子,你能够根据数据的重要性来交换性能以得到更强的持久性保证

4个Kafka broker,副本因子是3
可是你可能会问:producer或者consumer怎么知道partition leader是谁?

对生产者/消费者对分区的写/读请求,它们须要知道分区的leader是哪个,对吧?这个信息确定是能够获取到的,Kafka使用zookeeper来存储这些元数据。

什么是ZooKeeper

Zookeeper是一个分布式键值存储。它针对读取进行了高度优化,但写入速度较慢。它最经常使用于存储元数据和处理群集的机制(心跳,分发更新/配置等)。

它容许服务的客户(Kafka broker)订阅并在发生变动后发送给他们,这就是Kafka如何知道什么时候切换分区领导者。ZooKeeper自己维护了一个集群,因此它就有很高的容错性,固然它也应该具备,毕竟Kafka很大程度上是依赖于它的。

zookeeper用于存储全部的元数据信息,包括但不限于以下几项:

  • 消费者组每一个分区的偏移量(如今客户端在单独的kafka topic上存储偏移量)
  • ACL —— 权限控制
  • 生产者/消费者的流量控制——每秒生产/消费的数据大小。能够参考Kafka-流量控制Quota功能
  • partition leader以及它们的健康信息

那么produer/consumer是如何知道谁是partition leader的呢?

生产者和消费者之前经常直接链接ZooKeeper来获取这些信息,可是Kafka从0.8和0.9版本开始移除了这种强耦合关系。客户端直接从kafka broker直接获取这些元数据,而让kafka broker从zookeeper那里获取这些元数据。

获取leader

更多zookeeper的讲解能够参考:漫画:什么是ZooKeeper?

流式处理(Streaming)

在Kafka中,流处理器是指从输入主题获取连续数据流,对此输入执行某些处理并生成数据流以输出到其余主题(或者外部服务,数据库,容器等等).

什么是数据流呢?首先,数据流是无边界数据集的抽象表示。无边界意味着无限和持续增加。无边界数据集之因此是无限的,是由于随着时间推移,新的记录会不断加入进来。好比信用卡交易,股票交易等事件均可以用来表示数据流

咱们可使用producer/consumer的API直接进行简单处理,可是对于更加复杂的转换好比将流链接到一块儿,kafka提供了集成Stream API

这个API是在你本身的代码中使用的,它并非运行在broker上,它的工做原理和consumer API相似,可帮助你在多个应用程序(相似于消费者组)上扩展流处理工做。

无状态处理

流的无状态处理是肯定性处理,其不依赖于任何外部条件,对于任何给定的数据,将始终生成与其余任何内容无关的相同输出。举个例子,咱们要作一个简单的数据转换----"zhangsan" ---> "Hello,zhangsan"

kafka流处理

流-表二义性

重要的是要认识到流和表实质上是同样的,流能够被解释称为表,表也能够被解释称为流.

流做为表

流能够解释为数据的一系列更新,聚合后得结果就是表的最终结果,这项技术被称为事件溯源(Event Sourcing)

若是你了解数据库备份同步,你就会知道它们得技术实现被称为流式复制----将对表的每一个更改都发送报副本服务器.好比redis中的AOF以及Mysql中的binlog

Kafka流能够用相同的方式解释 - 当累积造成最终状态时的事件。此类流聚合保存在本地RocksDB中(默认状况下),被称为KTable。

Kafka流转换为表

表做为流

能够将表视为流中每一个键的最新值的快照。以流记录能够生成表同样,表更新能够生成更改日志流。

kafka表做为流

有状态处理

咱们在java中经常使用的一些操做好比map()或者filter()是没有状态的,它不会要求你保留任何原始数据。可是现实中,大多数的操做都是有状态的(好比count()),由于就须要你存储当前累计的状态。

在流处理器上维护状态的问题是流处理器可能会失败!你须要在哪里保持这种状态才能容错?

一种简单的方法是简单地将全部状态存储在远程数据库中,并经过网络链接到该存储,这样作的问题是大量的网络带宽会使得你的应用程序变慢。一个更微妙但重要的问题是你的流处理做业的正常运行时间将与远程数据库紧密耦合,而且做业将不是自包含的(其余team更改数据库可能会破坏你的处理)。

那么什么是更好的办法呢? 回想一下表和流的二元性。这容许咱们将流转换为与咱们的处理位于同一位置的表。它还为咱们提供了一种处理容错的机制 - 经过将流存储在Kafka broker中。

流处理器能够将其状态保持在本地表(例如RocksDB)中,该表将从输入流(可能在某些任意转换以后)更新。当进程失败时,它能够经过重放流来恢复其数据。

你甚至能够将远程数据库做为流的生产者,有效地广播用于在本地重建表的更改日志。

Kafka处理有状态数据

KSQL

一般,咱们不得不使用JVM语言编写流处理,由于这是惟一的官方Kafka Streams API客户端。 2018年4月,KSQL做为一项新特性被发布,它容许你使用熟悉的相似SQL的语言编写简单的stream jobs。你安装了KSQL服务器并经过CLI以交互方式查询以及管理。它使用相同的抽象(KStream和KTable),保证了Streams API的相同优势(可伸缩性,容错性),并大大简化了流的工做。

这听起来可能不是不少,但在实践中对于测试内容更有用,甚至容许开发以外的人(例如产品全部者)使用流处理,能够看看Confluent提供的这篇关于ksql的使用

何时使用kafka

正如咱们已经介绍的那样,Kafka容许你经过集中式介质获取大量消息并存储它们,而没必要担忧性能或数据丢失等问题。 这意味着它很是适合用做系统架构的核心,充当链接不一样应用程序的集中式媒体。Kafka能够成为事件驱动架构的中心部分,使你能够真正地将应用程序彼此分离.

何时使用Kafka
Kafka容许你轻松地分离不一样(微)服务之间的通讯。使用Streams API,如今能够比以往更轻松地编写业务逻辑,从而丰富Kafka主题数据以供服务使用。可能性很大,我恳请你探讨公司如何使用Kafka。

总结

Apache Kafka是一个分布式流媒体平台,天天可处理数万亿个事件。Kafka提供低延迟,高吞吐量,容错的发布和订阅管道,并可以处理事件流。咱们回顾了它的基本语义(生产者,代理,消费者,主题),了解了它的一些优化(pagecache),经过复制数据了解了它的容错能力,并介绍了它不断增加的强大流媒体功能。Kafka已经在全球数千家公司中大量采用,其中包括财富500强企业中的三分之一。随着Kafka的积极开发和最近发布的第一个主要版本1.0(2017年11月1日),有预测这个流媒体平台将会与关系数据库同样,是数据平台的重要核心。我但愿这篇介绍能帮助你熟悉Apache Kafka。

参考文章

tech.meituan.com/2015/01/13/… shiyueqi.github.io/2017/04/27/… kafka.apache.org/documentati… docs.confluent.io/current/

相关文章
相关标签/搜索