[Kafka ]全面介绍Apache Kafka™
深入研究作为许多公司架构核心的系统
介绍
卡夫卡是一个现在听到很多的话......许多领先的数字公司似乎也在使用它。但究竟是什么呢?
Kafka最初于2011年在LinkedIn开发,自那时起经历了很多改进。如今它是一个完整的平台,允许您冗余地存储荒谬的数据量,拥有一个具有巨大吞吐量(数百万/秒)的消息总线,并对同时通过它的数据使用实时流处理。
Kafka是一个分布式,可水平扩展,容错的提交日志。
那是一些奇特的话,让我们一个接一个地看看他们的意思。之后,我们将深入探讨它的工作原理。
分布式
分布式系统是分成多个运行的计算机的系统,所有这些计算机在一个集群中一起工作,作为最终用户的一个单一节点出现。 Kafka的分布在于它在不同节点(称为代理)上存储,接收和发送消息。
我也对此有一个全面的介绍
这种方法的好处是高可扩展性和容错性。
水平可扩展性
我们首先定义术语垂直可伸缩性。比如说,你有一个传统的数据库服务器开始变得过载。解决这个问题的方法是简单地增加服务器上的资源(CPU,RAM,SSD)。这称为垂直缩放 - 您可以向机器添加更多资源。向上扩展有两大缺点:
- 硬件定义了限制。你不能无限期地向上扩展。
- 它通常需要停机时间,这是大公司无法承受的。
水平可扩展性通过向其投入更多机器来解决同样的问题。添加新计算机不需要停机,也不会限制群集中的计算机数量。问题在于并非所有系统都支持水平可伸缩性,因为它们不是设计用于集群中,而是那些通常更复杂的系统。
Horizontal scaling becomes much cheaper after a ce
容错
非分布式系统中出现的一点是它们具有单点故障(SPoF)。 如果您的单个数据库服务器由于某种原因而失败(正如机器那样),那就搞砸了。
分布式系统的设计方式是以可配置的方式适应故障。 在5节点Kafka群集中,即使其中2个节点关闭,您也可以继续工作。 值得注意的是,容错与性能直接相关,因为在您的系统容错程度越高时,性能就越差。
提交日志
提交日志(也称为预写日志,事务日志)是仅支持附加的持久有序数据结构。 您无法修改或删除记录。 它从左到右阅读并保证项目订购。
Sample illustration of a commit log
- 你是在告诉我Kafka是如此简单的数据结构吗?
在很多方面,是的。这种结构是卡夫卡的核心,非常宝贵,因为它提供了排序,而排序则提供了确定性的处理。这两者都是分布式系统中的重要问题。
Kafka实际上将所有消息存储到磁盘(稍后会详细介绍),并在结构中对它们进行排序,以便利用顺序磁盘读取。
读取和写入是一个恒定时间O(1)(知道记录ID),与磁盘上其他结构的O(log N)操作相比是一个巨大的优势,因为每次磁盘搜索都很昂贵。
读取和写入不会影响另一个。写作不会锁定读数,反之亦然(与平衡树相对)
这两点具有巨大的性能优势,因为数据大小与性能完全分离。无论您的服务器上有100KB还是100TB的数据,Kafka都具有相同的性能。
它是如何工作的?
应用程序(生产者)将消息(记录)发送到Kafka节点(代理),并且所述消息由称为消费者的其他应用程序处理。所述消息存储在主题中,并且消费者订阅该主题以接收新消息。
随着主题变得非常大,它们会分成更小的分区,以获得更好的性能和可伸缩性。 (例如:假设您存储了用户登录请求,您可以按用户用户名的第一个字符拆分它们)
Kafka保证分区内的所有消息都按照它们进入的顺序排序。区分特定消息的方式是通过其偏移量,您可以将其视为普通数组索引,序列号对于每个新消息递增 在一个分区。
卡夫卡遵循愚蠢的经纪人和聪明的消费者的原则。 这意味着Kafka不会跟踪消费者读取的记录并删除它们,而是将它们存储一定的时间(例如一天)或直到满足某个大小阈值。 消费者自己向卡夫卡民意调查新消息,并说出他们想要阅读的记录。 这允许它们按照自己的意愿递增/递减它们所处的偏移量,从而能够重放和重新处理事件。
值得注意的是,消费者实际上是消费者群体,其中包含一个或多个消费者流程。 为了避免两个进程两次读取相同的消息,每个分区仅与每个组的一个消费者进程相关联。
持久化到磁盘
正如我之前提到的,Kafka实际上将所有记录存储到磁盘中,并且不会在RAM中保留任何内容。你可能想知道这是如何以最明智的方式做出明智的选择。这背后有许多优化使其可行:
- Kafka有一个将消息组合在一起的协议。这允许网络请求将消息组合在一起并减少网络开销,服务器反过来一次性保留大量消息,消费者一次获取大型线性块
- 磁盘上的线性读/写速度很快。现代磁盘速度慢的概念是由于大量磁盘搜索,这在大型线性操作中不是问题。
- 所述线性操作由OS大量优化,通过预读(预取大块倍数)和后写(组小逻辑写入大物理写入)技术。
- 现代操作系统将磁盘缓存在空闲RAM中。这称为pagecache。
- 由于Kafka在整个流程(生产者 - >代理 - >消费者)中以未经修改的标准化二进制格式存储消息,因此它可以使用零拷贝优化。那时操作系统将数据从pagecache直接复制到套接字,有效地完全绕过了Kafka代理应用程序。
所有这些优化都使Kafka能够以接近网络的速度传递消息。
数据分发和复制
我们来谈谈Kafka如何实现容错以及它如何在节点之间分配数据。
数据复制
分区数据在多个代理中复制,以便在一个代理程序死亡时保留数据。
在任何时候,一个代理“拥有”一个分区,并且是应用程序从该分区写入/读取的节点。这称为分区领导者。它将收到的数据复制到N个其他经纪人,称为追随者。它们也存储数据,并准备好在领导节点死亡时被选为领导者。
这有助于您配置保证任何成功发布的消息都不会丢失。通过选择更改复制因子,您可以根据数据的重要性来交换性能以获得更强的持久性保证。
通过这种方式,如果一个领导者失败,追随者可以取代他的位置。
不过你可能会问:
- 生产者/消费者如何知道分区的领导者是谁?
对于生产者/消费者来说,从分区写入/读取,他们需要知道它的领导者,对吗?这些信息需要从某个地方获得。
Kafka将这些元数据存储在名为Zookeeper的服务中。
什么是Zookeeper?
Zookeeper是一个分布式键值存储。它针对读取进行了高度优化,但写入速度较慢。它最常用于存储元数据和处理群集的机制(心跳,分发更新/配置等)。
它允许服务的客户(Kafka经纪人)订阅并在发生变更后发送给他们。这就是经纪人如何知道何时切换分区领导者。动物园管理员也非常容错,应该是,因为卡夫卡在很大程度上依赖它。
它用于存储所有类型的元数据,提到一些:
- 消费者群体的每个分区的偏移量(尽管现代客户端在单独的Kafka主题中存储偏移量)
- ACL(访问控制列表) - 用于限制访问/授权
- 生产者和消费者配额 - 最大消息/秒边界
- 分区领导者及其健康
生产者/消费者如何知道分区的领导者是谁?
生产者和消费者过去常常直接连接并与Zookeeper交谈以获取此(和其他)信息。 Kafka已经远离这种耦合,从版本0.8和0.9开始,客户端直接从Kafka经纪人那里获取元数据信息,他们自己与Zookeeper交谈。
流
在Kafka中,流处理器是从输入主题获取连续数据流,对此输入执行一些处理并生成数据流以输出主题(或外部服务,数据库,垃圾箱,无论何处......)的任何内容。
可以直接使用生产者/消费者API进行简单处理,但是对于更复杂的转换(如将流连接在一起),Kafka提供了一个集成的Streams API库。
此API旨在用于您自己的代码库中,而不是在代理上运行。它与消费者API类似,可帮助您在多个应用程序(类似于消费者组)上扩展流处理工作。
无状态处理
流的无状态处理是确定性处理,其不依赖于任何外部。您知道,对于任何给定的数据,您将始终生成与其他任何内容无关的相同输出。一个例子就是简单的数据转换 - 将某些内容附加到字符串“Hello” - >“Hello,World!”。
流表双重性
重要的是要认识到流和表基本相同。 流可以解释为表,表可以解释为流。
流作为表
流可以解释为数据的一系列更新,其中聚合是表的最终结果。 这种技术称为事件采购。
如果您了解如何实现同步数据库复制,您将看到它是通过所谓的流复制,其中表中的每个更改都发送到副本服务器。 事件采购的另一个例子是区块链分类账 - 分类账也是一系列变化。
Kafka流可以用相同的方式解释 - 当累积形成最终状态时的事件。 此类流聚合保存在本地RocksDB中(默认情况下),称为KTable。
表作为流
可以将表视为流中每个键的最新值的快照。 以相同的方式,流记录可以生成表,表更新可以生成更改日志流。
有状态处理
一些简单的操作(如map()或filter())是无状态的,不需要您保留有关处理的任何数据。但是,在现实生活中,您所做的大多数操作都是有状态的(例如count()),因此需要您存储当前累积的状态。
在流处理器上维护状态的问题是流处理器可能会失败!你需要在哪里保持这种状态才能容错?
一种简单的方法是简单地将所有状态存储在远程数据库中,并通过网络连接到该存储。这样做的问题是没有数据的位置和大量的网络往返,这两者都会显着减慢您的应用程序。一个更微妙但重要的问题是您的流处理作业的正常运行时间将紧密耦合到远程数据库,并且作业将不会自包含(数据库中的数据库与另一个团队的更改可能会破坏您的处理)。
那么什么是更好的方法呢?
回想一下表和流的二元性。这允许我们将流转换为与我们的处理位于同一位置的表。它还为我们提供了一种处理容错的机制 - 通过将流存储在Kafka代理中。
流处理器可以将其状态保持在本地表(例如RocksDB)中,该表将从输入流(可能在某些任意转换之后)更新。当进程失败时,它可以通过重放流来恢复其数据。
您甚至可以将远程数据库作为流的生产者,有效地广播用于在本地重建表的更改日志。
KSQL
通常,您将被迫使用JVM语言编写流处理,因为这是唯一的官方Kafka Streams API客户端。
发布于2018年4月,KSQL是一项功能,允许您使用熟悉的类似SQL的语言编写简单的流媒体作业。
您设置了KSQL服务器并通过CLI以交互方式查询它以管理处理。它使用相同的抽象(KStream和KTable),保证了Streams API的相同优点(可伸缩性,容错性),并大大简化了流的工作。
这听起来可能不是很多,但在实践中对于测试内容更有用,甚至允许开发之外的人(例如产品所有者)使用流处理。我鼓励您查看快速启动视频,看看它有多简单。
流替代品
Kafka溪流是力量与简约的完美结合。它可以说是市场上流媒体工作的最佳功能,它与其他流处理选择(Storm,Samza,Spark,Wallaroo)相比,更容易与Kafka集成。
大多数其他流处理框架的问题在于它们使用和部署起来很复杂。像Spark这样的批处理框架需要:
- 在一组计算机上控制大量作业,并在整个集群中有效地分配它们。
- 为此,它必须动态地打包您的代码并将其物理部署到将执行它的节点。 (以及配置,库等)
不幸的是,解决这些问题使框架非常具有侵略性。他们希望控制代码的部署,配置,监控和打包方式的许多方面。
Kafka Streams允许您在需要时推出自己的部署策略,无论是Kubernetes,Mesos,Nomad,Docker Swarm还是其他人。
Kafka Streams的基本动机是使所有应用程序能够进行流处理,而无需运行和维护另一个集群的操作复杂性。唯一潜在的缺点是它与卡夫卡紧密结合,但在现代世界中,大多数(如果不是全部)实时处理由卡夫卡提供动力可能不是一个很大的劣势。
你什么时候用Kafka?
正如我们已经介绍的那样,Kafka允许您通过集中式介质获取大量消息并存储它们,而不必担心性能或数据丢失等问题。
这意味着它非常适合用作系统架构的核心,充当连接不同应用程序的集中式媒体。 Kafka可以成为事件驱动架构的中心部分,使您可以真正地将应用程序彼此分离。
Kafka允许您轻松地分离不同(微)服务之间的通信。使用Streams API,现在可以比以往更轻松地编写业务逻辑,从而丰富Kafka主题数据以供服务使用。可能性很大,我恳请您探讨公司如何使用Kafka。
它为什么看到这么多用途?
仅凭高性能,可用性和可扩展性并不足以使公司采用新技术。还有其他系统具有类似的特性,但没有一个被广泛使用。这是为什么?
Kafka越来越受欢迎(并且继续这样做)的原因是一个关键因素 - 现在的企业从事件驱动的架构中受益匪浅。这是因为世界已经发生了变化 - 许多不同的服务(物联网,机器学习,移动,微服务)正在生产和消费大量(并且不断增长)的数据量。
具有持久存储的单个实时事件广播平台是实现这种架构的最简洁方式。想象一下,如果每个服务之间的流数据使用了一种特别适合它的不同技术,那将会是一种混乱。
这与Kafka为这样的通用系统(持久存储,事件广播,表和流原语,通过KSQL进行抽象,开源,积极开发)提供适当特性的事实相结合,使其成为公司的明显选择。
摘要
Apache Kafka是一个分布式流媒体平台,每天可处理数万亿个事件。 Kafka提供低延迟,高吞吐量,容错的发布和订阅管道,并能够处理事件流。
我们回顾了它的基本语义(生产者,代理,消费者,主题),了解了它的一些优化(pagecache),通过复制数据了解了它的容错能力,并介绍了它不断增长的强大流媒体功能。
Kafka已经在全球数千家公司中大量采用,其中包括财富500强企业中的三分之一。随着Kafka的积极开发和最近发布的第一个主要版本1.0(2017年11月1日),有预测这个流媒体平台将会与关系数据库一样,是数据平台的重要核心。
我希望这篇介绍能帮助您熟悉Apache Kafka及其潜力。
进一步阅读资源和我没有提到的事情
以下是我没有机会提到的一些功能,但重要的是要知道:
- Controller Broker,同步副本 - Kafka保持集群健康并确保足够的一致性和持久性的方式。
- Connector API - API帮助您将各种服务连接到Kafka作为源或接收器(PostgreSQL,Redis,ElasticSearch)
- 日志压缩 - 减少日志大小的优化。在更改日志流中非常有用
- 完全一次的消息语义 - 保证消息只被接收一次。这是一个大问题,因为很难实现。
资源
- 81 次浏览