大家好,我是 kafka, 可能很多人都据说过我,晓得我是 2011 年出世在 LinkedIn 的, 从那会儿到当初我的性能越发弱小了。作为一个优良而又残缺的平台,你能够在我下面冗余地存储微小的数据量,我有一个具备高吞吐量 (数百万 / 秒) 的音讯总线,你能够在这上面对通过我的数据进行实时流解决。
如果你认为我就只有下面的这些特点的话,那么你真的是太浮浅了。
下面尽管说的很好,然而并未涉及到我的外围,这里我给你几个关键字:分布式,程度可扩大,容错,提交日志。
下面这些形象的词语,我会一一解释它们的含意,并通知你们我是如何工作的。
内心独白: 原本我是想要以第一人称来写这篇文章的,然而我发现我只能写出下面的了,再多的我就憋不进去了,于是我决定不要尴尬本人,还是用用第三人称写吧 (写作的功底依然须要锤炼)
分布式
分布式系统由多个运行的计算机系统组成,所有这些计算机在一个集群中一起工作,对终端用户来讲只是一个繁多节点。
kafka也是分布式的,因为它在不同的节点(又被称为broker)上存储,承受以及发送音讯,这样做的益处是具备很高的可扩展性和容错性。
程度可扩展性
在这之前,先看看什么是垂直可扩大,比方你有一个传统的数据库服务器,它开始适度负载,解决这个问题的方法就是给服务器加配置(cpu,内存,SSD),这就叫做垂直扩大。然而这种形式存在两个微小的劣势
- 硬件存在限度,不可能有限的增加机器配置
- 它须要停机工夫,通常这是很多公司无奈容忍的
程度可扩大就是通过增加更多的机器来解决同样的问题,增加新机器不须要停机,而且集群中也不会对机器的数量有任何的限度。问题在于并非所有零碎都反对程度可伸缩性,因为它们不是设计用于集群中(集群中工作更加简单)。
容错性
非分布式系统中容易最致命的问题就是单点失败,如果你惟一的服务器挂掉了,那么我置信你会很解体。
而分布式系统的设计形式就是能够以配置的形式来答应失败。在5个节点的kafka集群中,你依然能够持续工作即便其中两个节点挂掉了。
须要留神的是,容错与性能间接相干,你的零碎容错水平越高,性能就越差。
提交日志(commit log)
提交日志(也被称为预写日志或者事物日志)是仅反对附加的长久有序数据结构,你无奈批改或者删除记录,它从左往右读并且保障日志的程序。
是不是感觉kafka的数据结构如此简略?
是的,从很多方面来讲,这个数据结构就是kafka的外围。这个数据结构的记录是有序的,而有序的数据能够确保咱们的解决流程。这两个在分布式系统中都是及其重要的问题。
kafka实际上将所有音讯存储到磁盘并在数据结构中对它们进行排序,以便利用程序磁盘读取。
- 读取和写入都是常量工夫O(1)(当确定了record id),与磁盘上其余构造的O(log N)操作相比是一个微小的劣势,因为每个磁盘搜寻都很耗时。
- 读取和写入不会相互影响,写不会锁住读,反之亦然。
这两点有着微小的劣势, 因为数据大小与性能齐全拆散。无论你的服务器上有100KB还是100TB的数据,Kafka都具备雷同的性能
如何工作
应用程序(producer)发送音讯(record)到kafka服务器(broker),这些音讯会被其余应用程序(consumer)所解决,这些音讯存储在主题(topic)中,并且消费者订阅该主题以接管新音讯。是不是感觉很像你平时写的代码——生产者消费者模式。
随着主题变得十分大,它们会分成更小的分区(partition),以取得更好的性能和可伸缩性(比方存储了用户互相发送的音讯,你能够依据用户名的第一个字母来进行拆分)。Kafka保障分区内的所有音讯都依照它们的程序排序,辨别特定音讯的形式是通过其偏移量(offset),你能够将其视为一般数组索引,即为分区中的每个新音讯递增的序列号。
kafka恪守着愚昧的broker和聪慧的consumer的准则。这意味着kafka不会跟踪消费者读取了哪些记录并删除它们,而是会将它们存储肯定的工夫(比方1天,以log.retention结尾的来决定日志保留工夫),直到达到某个阈值。消费者本人轮询kafka的新音讯并且通知它本人想要读取哪些记录。这容许它们依照本人的志愿递增/递加它们所处的偏移量,从而可能重放和从新处理事件。
须要留神的是消费者是属于消费者组的,消费者组有一个或多个消费者。为了防止两个过程读取同样的音讯两次,每个partition只能被一个消费者组中的一个消费者拜访。
长久化到硬盘
正如我之前提到的,kafka实际上是将所有记录存储到硬盘而不在RAM中保留任何内容。你想晓得这个如何做出这个抉择的,其实这背地有很多优化使得这个计划可行。
- kafka有一个将音讯分组的协定,这容许网络申请将音讯组合在一起并缩小网络开销,服务器反过来一次性保留大量音讯,消费者一次获取大量线性块。
- 磁盘上线性读写十分快,古代磁盘十分慢的概念是因为大量磁盘寻址,然而在大量的线性操作中不是问题。
- 操作系统对线性操作进行了大量优化,通过预读(预取大块屡次)和后写(将小型逻辑写入组成大型物理写入)技术。
操作系统将磁盘文件缓存在闲暇RAM中。这称为pagecache,而kafka的读写都大量应用了pagecahce
- 写音讯的时候音讯先从java到page cache,而后异步线程刷盘,音讯从page cache刷入磁盘
- 读音讯的时候先从page cache找,有就间接转入socket,没有就先从磁盘load到page cache,而后间接从socket收回去
- 因为Kafka在整个流程(producer - >broker - >consumer)中以未经批改的标准化二进制格局存储音讯,因而它能够应用零拷贝优化。那时操作系统将数据从pagecache间接复制到socket,无效地齐全绕过了Kafka broker。
所有这些优化都使Kafka可能以靠近网络的速度传递音讯。
数据散发和复制
咱们来谈谈Kafka如何实现容错以及它如何在节点之间调配数据。
为了使得一个borker挂掉的时候,数据还能得以保留,分区(partition)数据在多个broker中复制。
在任何时候,一个broker领有一个partition,应用程序读取/写入都要通过这个节点,这个节点叫做----partition leader。它将收到的数据复制到N个其余broker,这些接收数据的broker叫做follower,follower也存储数据,一旦leader节点死掉的时候,它们就筹备竞争上岗成为leader。
这能够保障你胜利公布的音讯不会失落,通过抉择更改复制因子,你能够依据数据的重要性来替换性能以取得更强的持久性保障
然而你可能会问: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那里获取这些元数据。
更多zookeeper的解说能够参考:漫画:什么是ZooKeeper?
流式解决(Streaming)
在Kafka中,流处理器是指从输出主题获取间断数据流,对此输出执行某些解决并生成数据流以输入到其余主题(或者内部服务,数据库,容器等等).
什么是数据流呢?首先,数据流是无边界数据集的形象示意。无边界意味着有限和持续增长。无边界数据集之所以是有限的,是因为随着时间推移,新的记录会一直退出进来。比方信用卡交易,股票交易等事件都能够用来示意数据流
咱们能够应用producer/consumer的API间接进行简略解决,然而对于更加简单的转换比方将流连接到一起,kafka提供了集成Stream API库
这个API是在你本人的代码中应用的,它并不是运行在broker上,它的工作原理和consumer API相似,可帮忙你在多个应用程序(相似于消费者组)上扩大流解决工作。
无状态解决
流的无状态解决是确定性解决,其不依赖于任何内部条件,对于任何给定的数据,将始终生成与其余任何内容无关的雷同输入。举个例子,咱们要做一个简略的数据转换----"zhangsan" ---> "Hello,zhangsan"
流-表二义性
重要的是要意识到流和表本质上是一样的,流能够被解释称为表,表也能够被解释称为流.
流作为表
流能够解释为数据的一系列更新,聚合后得后果就是表的最终后果,这项技术被称为事件溯源(Event Sourcing)
如果你理解数据库备份同步,你就会晓得它们得技术实现被称为流式复制----将对表的每个更改都发送报正本服务器.比方redis中的AOF以及Mysql中的binlog
Kafka流能够用雷同的形式解释 - 当累积造成最终状态时的事件。此类流聚合保留在本地RocksDB中(默认状况下),被称为KTable。
表作为流
能够将表视为流中每个键的最新值的快照。以流记录能够生成表一样,表更新能够生成更改日志流。
有状态解决
咱们在java中罕用的一些操作比方map()或者filter()是没有状态的,它不会要求你保留任何原始数据。然而事实中,大多数的操作都是有状态的(比方count()),因为就须要你存储以后累计的状态。
在流处理器上保护状态的问题是流处理器可能会失败!你须要在哪里放弃这种状态能力容错?
一种简略的办法是简略地将所有状态存储在近程数据库中,并通过网络连接到该存储,这样做的问题是大量的网络带宽会使得你的应用程序变慢。一个更奥妙但重要的问题是你的流解决作业的失常运行工夫将与近程数据库严密耦合,并且作业将不是自蕴含的(其余team更改数据库可能会毁坏你的解决)。
那么什么是更好的方法呢?
回忆一下表和流的二元性。这容许咱们将流转换为与咱们的解决位于同一地位的表。它还为咱们提供了一种解决容错的机制 - 通过将流存储在Kafka broker中。
流处理器能够将其状态放弃在本地表(例如RocksDB)中,该表将从输出流(可能在某些任意转换之后)更新。当过程失败时,它能够通过重放流来复原其数据。
你甚至能够将近程数据库作为流的生产者,无效地播送用于在本地重建表的更改日志。
KSQL
通常,咱们不得不应用JVM语言编写流解决,因为这是惟一的官网Kafka Streams API客户端。
2018年4月,KSQL作为一项新个性被公布,它容许你应用相熟的相似SQL的语言编写简略的stream jobs。你装置了KSQL服务器并通过CLI以交互方式查问以及治理。它应用雷同的形象(KStream和KTable),保障了Streams API的雷同长处(可伸缩性,容错性),并大大简化了流的工作。
这听起来可能不是很多,但在实践中对于测试内容更有用,甚至容许开发之外的人(例如产品所有者)应用流解决,能够看看Confluent提供的这篇对于ksql的应用
什么时候应用kafka
正如咱们曾经介绍的那样,Kafka容许你通过集中式介质获取大量音讯并存储它们,而不用放心性能或数据失落等问题。
这意味着它非常适合用作零碎架构的外围,充当连贯不同应用程序的集中式媒体。Kafka能够成为事件驱动架构的核心局部,使你能够真正地将应用程序彼此拆散.
Kafka容许你轻松地拆散不同(微)服务之间的通信。应用Streams API,当初能够比以往更轻松地编写业务逻辑,从而丰盛Kafka主题数据以供服务应用。可能性很大,我恳请你探讨公司如何应用Kafka。
总结
Apache Kafka是一个分布式流媒体平台,每天可解决数万亿个事件。Kafka提供低提早,高吞吐量,容错的公布和订阅管道,并可能处理事件流。咱们回顾了它的根本语义(生产者,代理,消费者,主题),理解了它的一些优化(pagecache),通过复制数据理解了它的容错能力,并介绍了它一直增长的弱小流媒体性能。Kafka曾经在寰球数千家公司中大量采纳,其中包含财产500强企业中的三分之一。随着Kafka的踊跃开发和最近公布的第一个次要版本1.0(2017年11月1日),有预测这个流媒体平台将会与关系数据库一样,是数据平台的重要外围。我心愿这篇介绍能帮忙你相熟Apache Kafka。
参考文章
https://tech.meituan.com/2015...
https://shiyueqi.github.io/20...
http://kafka.apache.org/docum...
https://docs.confluent.io/cur...