本译文自Jean-Paul Azar 在 https://dzone.com 发表的 Kafka Detailed Design and Ecosystem ,文中版权,图像代码的数据均归作者所有。为了本土化,翻译内容略作修改。
Kafka的核心是经纪人,主题,日志,分区和集群。核心也包括像MirrorMaker这样的相关工具。前面提到的是Kafka,因为它存在于Apache中。
Kafka生态系统由Kafka Core,Kafka Streams,Kafka Connect,Kafka REST Proxy和Schema Registry组成。大部分Kafka生态系统的其他部分来自Confluent,不属于Apache。
Kafka Stream是Streams API,用于转换、汇总和处理来自流的记录并生成衍生流。Kafka Connect是API连接器,用于创建可重用的生产者和消费者(例如,来自DynamoDB的更改流)。Kafka REST代理通过REST(HTTP)被用于生产者和消费者。该架构注册管理使用模式的AvroKafka的记录。Kafka MirrorMaker用于将群集数据复制到另一个群集。
Kafka连接源是记录的来源。Kafka连接水槽是记录的目的地。
Kafka Stream API基于核心Kafka原语构建,并拥有自己的生命。Kafka流可以实时处理流。Kafka Streams支持流处理器。流处理器从输入主题获取连续的记录流,对输入执行一些处理,转换和聚合,并产生一个或多个输出流。例如,视频播放器应用程序可能会接收观看的视频事件的输入流,并暂停视频,并输出用户偏好流,然后基于最近的用户活动或许多用户的聚合活动来获取新的视频推荐,以查看哪些新的视频很热。Kafka Stream API解决了无序记录的难题,跨多个流聚合,连接来自多个流的数据,允许有状态的计算等等。
Kafka流可以实时处理流。它可以聚合多个流,连接来自多个流的数据,允许有状态的计算等等。
Kafka Connect是连接器API,用于创建可重用的生产者和消费者(例如,来自DynamoDB的更改流)。Kafka连接源是记录的来源。Kafka连接水槽是记录的目的地。
模式注册管理使用Avro作为Kafka记录管理模式。
Kafka MirrorMaker用于将群集数据复制到另一个群集。
Kafka REST代理通过REST(HTTP)被用于生产者和消费者。您可以使用它来轻松整合现有的代码库。
这篇文章从我们关于Kafka架构的系列文章中有所体现,其中包括Kafka主题架构,Kafka制作者架构, Kafka用户架构和Kafka生态系统架构。
本文受到Kafka设计部分的启发。你可以把它想成悬崖笔记。
LinkedIn工程师构建Kafka以支持实时分析。Kafka被设计为提供实时处理流的分析系统。LinkedIn将Kafka开发为实时处理流式数据馈送的统一平台。Kafka背后的目标是构建一个高吞吐量的流媒体数据平台,支持日志聚合,用户活动等大容量事件流。
为了满足Kafka的需求扩展,分布式支持分片和负载均衡。扩展需求激发了Kafka的分区和消费者模型。Kafka使用分区,分布式,提交日志来扩展写入和读取。Kafka的分片被称为分区(Kinesis,类似于Kafka,称为分区“碎片”)。
根据维基百科的说法,“数据库分片是数据库或搜索引擎中数据的水平分区,每个分区被称为分片或数据库分片,每个分片被保存在一个单独的数据库服务器实例上,以传播负载”。
Kafka被设计为处理来自离线系统的周期性大数据加载以及传统的消息传递用例,低延迟。
MOM是面向消息的中间件; 考虑IBM MQSeries,JMS,ActiveMQ和RabbitMQ。像许多MOM一样,Kafka通过复制和领导选举来节点故障的容错。但是,Kafka的设计更像是一个分布式的数据库事务日志而不是传统的消息传递系统。与许多MOM不同的是,Kafka的复制是建立在低级设计之上的,并不是事后的想法。
Kafka依靠文件系统来存储和缓存记录。
顺序写入硬盘性能的硬盘性能很快(非常快)。JBOD只是一堆磁盘驱动器。带有6个7200rpm SATA RAID-5阵列的JBOD配置约为600MB /秒。像Cassandra表一样,Kafka日志是只写结构,意思是数据会被附加到日志的末尾。在使用硬盘驱动器时,顺序读取和写入速度快,可预测,并且可以通过操作系统进行大量优化。使用HDD时,顺序磁盘访问可能比随机存储器访问和SSD更快。
尽管JVM GC的开销可能会很高,但是Kafka在操作系统上依赖于缓存,这是一个巨大的,快速且稳定的缓存。而且,现代操作系统使用所有可用的主存储器来进行磁盘缓存。操作系统文件缓存几乎是免费的,没有操作系统的开销。实现高速缓存一致性是正确的挑战,但是Kafka依靠坚如磐石的操作系统来实现高速缓存一致性。使用OS进行缓存还会减少缓冲区副本的数量。由于Kafka磁盘使用趋向于顺序读取,所以OS预读缓存令人印象深刻。
Cassandra,Netty和Varnish使用类似的技术。所有这一切都在Kafka文件中得到了很好的解释,在油漆现场还有一个更有趣的解释。
Kafka主张长时间顺序访问磁盘进行读取和写入。像Cassandra,LevelDB,RocksDB和其他Kafka使用日志结构化存储和压缩的形式,而不是磁盘上可变的BTree。像Cassandra一样,Kafka使用墓碑而不是立即删除记录。
由于磁盘这些天有一些无限的空间,并且速度非常快,Kafka可以提供通常在消息系统中不常见的功能,如长时间保持旧消息。这种灵活性允许Kafka有趣的应用。
生产者向Kafka经纪人询问有关哪个Kafka经纪人具有哪个主题分区领导的元数据,因此不需要路由层。这个领导数据允许生产者直接向Kafka经纪人分区领导发送记录。
生产者客户端控制它将消息发布到哪个分区,并且可以根据某些应用程序逻辑选择一个分区。生产者可以通过密钥,循环法或使用定制应用程序特定的分区逻辑来分区记录。
Kafka生产商支持记录配料。批量可以通过批量记录的大小来配置。批次可以根据时间自动刷新。
批量处理对于网络IO吞吐量非常有利,并大幅提高吞吐量。
缓冲是可配置的,并允许您在更好的吞吐量之间进行额外延迟之间的权衡。或者在大量使用的系统的情况下,它可能是更好的平均吞吐量,并减少总体延迟。
批量处理允许累积更多的字节发送,相当于Kafka Brokers上较少的I / O操作,并提高了压缩效率。为了获得更高的吞吐量,Kafka Producer配置允许基于时间和大小的缓冲。生产者发送多个记录作为一个批次,而不是逐个发送每个记录的网络请求。
在大型流媒体平台中,瓶颈并不总是CPU或磁盘,而是通常网络带宽。云中存在更多的网络带宽问题,如集装箱化和虚拟化环境,因为多个服务可能共享一个NiC卡。另外,与数据中心或WAN通信时,网络带宽问题可能会有问题。
批处理有利于高效压缩和网络IO吞吐量。
Kafka提供了端到端的批量压缩,而不是一次压缩记录,Kafka有效地压缩了整批记录。相同的消息批处理可以一次压缩并发送到Kafka代理/服务器,并以压缩形式写入日志分区。您甚至可以配置压缩,以便在Kafka经纪商将压缩记录传送给用户之前不进行解压缩。
Kafka支持GZIP,Snappy和LZ4压缩协议。
Kafka消费者从经纪人那里获取数据。其他系统经纪商将数据或流数据推送给消费者。消息通常是一个基于拉的系统(SQS,大多数MOM使用拉)。在拉动式的情况下,如果消费者落后,它会在晚些时候赶上。
由于Kafka是基于拉式的,所以它实施了大量的数据分批处理。Kafka像许多基于拉的系统实现了长期民意调查(SQS,Kafka都这样做)。长时间轮询在请求一段时间后保持连接打开并等待响应。
一个基于拉的系统必须拉取数据然后处理它,拉和获取数据之间总是有一个暂停。
推送数据给消费者(抄写员,水槽,反应流,RxJava,Akka)。基于推送或流式传输系统在处理缓慢或死亡的消费者方面存在问题。当消费率低于生产速度时,推送系统消费者有可能不知所措。一些基于推送的系统使用基于背压的退避协议,其允许消费者指示其被压倒看到反应性流。当试图跟踪消息确认时,这种不会淹没消费者和消费者恢复的问题是棘手的。
基于推送或流式传输的系统可以立即发送请求,或者累积请求并批量发送(或基于反压的组合)。基于推送的系统总是在推送数据。消费者可以在处理已经发送的数据的同时累积消息,这有利于减少消息处理的延迟。但是,如果消费者在加工后死亡,那么经纪人如何知道消费者在哪里以及何时将数据再次发送给其他消费者。这个问题不是一个容易解决的问题。Kafka通过使用拉式系统来解决这些复杂问题。
对于大多数MOM,经纪人有责任跟踪哪些消息被标记为已消耗。消息跟踪不是一件容易的事情。随着消费者消费信息,经纪人会跟踪状态。
大多数MOM系统的目标是让经纪人在消费后快速删除数据。还记得大部分的MOM是在磁盘小得多,能力不足,价格昂贵的时候写的。
这个消息跟踪比听起来要复杂(确认功能),因为经纪人必须保持大量状态来跟踪每个消息,发送,确认并知道何时删除或重发消息。
请记住,Kafka主题分为有序分区。每条消息在此有序分区中都有一个偏移量。每个主题分区一次仅由一个消费者组消费。
这种分区布局的意思是,Broker跟踪每个消息跟踪的偏移数据,如MOM,但只需要每个用户组的偏移量,即存储的分区偏移对。这种偏移追踪等同于要追踪的数据少得多。
消费者定期向Kafka经纪人发送位置数据(消费者组,分区偏移对),经纪人将该偏移数据存储到偏移主题中。
与MOM相比,抵消风格的消息确认要便宜得多。另外,消费者更加灵活,可以倒退到更早的偏移(重放)。如果有错误,那么修复错误,倒回消费者并重播主题。这个倒带功能是Kafka的一个杀手功能,因为Kafka可以保存很长一段时间的主题日志数据。
有三种消息传递语义:最多一次,至少一次,恰好一次。最多一次的消息可能会丢失,但永远不会重新发送。至少一次消息是永远不会丢失的,但可以重新传递。每个消息恰好一次只传送一次。确切地说,曾经是首选的,但更昂贵的,并要求生产者和消费者更多的簿记。
回想一下,所有副本具有相同的偏移量的完全相同的日志分区,并且用户组在日志每个主题分区中保持其位置。
为了实现“最多一次”消费者读取消息,然后将其偏移保存在分区中,并将其发送给代理,最后处理该消息。“最多一次”的问题是消费者可能会在保存其位置之后,但在处理消息之前死亡。然后,接管或重新启动的消费者将在最后的位置离开,并且不会处理有问题的消息。
为了实现“至少一次”,消费者读取消息,处理消息,并最终将代价保存到代理。“至少一次”的问题是消费者在处理消息之后但在保存最后偏移位置之前可能崩溃。然后,如果消费者重新启动或其他消费者接管,消费者可能会收到已处理的消息。“至少一次”是最常见的消息传递设置,您的责任是使消息具有幂等性,这意味着两次获得相同的消息不会导致问题(两个借方)。
为了在消费者方面实现“恰好一次”,消费者需要在消费者位置的存储与消费者的消息处理输出的存储之间的两阶段提交。或者,消费者可以将消息处理输出存储在与最后偏移相同的位置。
Kafka提供了前两个,从消费者的角度来看,实现第三个。
Kafka为耐用性提供了可操作的可预测性语义。发布消息时,消息被“提交”到日志,这意味着所有ISR都接受消息。只要至少有一个副本存在,这个提交策略对于耐久性就能很好地工作。
生产者连接可能在发送过程中下降,生产者可能不确定它发送的消息是否经过,然后生产者重新发送消息。这个重发逻辑是为什么使用消息密钥和使用幂等消息(重复确定)是重要的。Kafka直到最近(2017年6月)才保证消息不会从生产者重试中复制。
生产者可以重新发送一个消息,直到收到确认,即收到确认。生产者重新发送消息而不知道其发送的其他消息是否与否,从而否定“恰好一次”和“最多一次”的消息传递语义。
制片人可以指定耐久度级别。制作人可以等待提交的消息。等待提交可确保所有副本都具有该消息的副本。
制片人可以发送没有确认(0)。生产者可以从分区领导(1)得到一个确认。生产者可以发送并等待来自所有副本(-1)的确认,这是默认的。
Kafka现在支持从生产者“精确地一次”交付,性能改进和分区间的原子写入。他们通过生产者发送一个序列ID来实现这一点,代理跟踪生产者是否已经发送了这个序列,如果生产者试图再次发送它,它会得到重复消息的确认,但是没有任何东西被保存到日志中。这种改进不需要API改变。
Kafka的另一个改进是Kafka生产者在原子笔划上进行分割。原子写入意味着Kafka用户只能看到提交日志(可配置)。Kafka有一个协调员,写一个标记到主题日志,以表示已经成功处理了什么。事务协调器和事务日志维护原子写入的状态。
原子写入确实需要一个新的生产者API来处理事务。
这是一个使用新的生产者API的例子。
producer.initTransaction();
try {
producer.beginTransaction();
producer.send(debitAccountMessage);
producer.send(creditOtherAccountMessage);
producer.sentOffsetsToTxn(...);
producer.commitTransaction();
} catch (ProducerFencedTransactionException pfte) {
...
producer.close();
} catch (KafkaException ke) {
...
producer.abortTransaction();
}
Kafka通过可配置数量的Kafka经纪人复制每个主题的分区。Kafka的复制模式是默认的,而不是像大多数MOM那样的插入功能,因为Kafka从一开始就打算使用分区和多节点。每个主题分区都有一个领导者和零个或多个关注者。
领导者和追随者被称为复制品。复制因素是领导者节点加上所有的追随者。分区领导在Kafka经纪人之间平均分享。消费者只能从领导读取。制片人只写信给领导。
追随者的主题日志分区与领导者的日志同步,ISR是领导者的精确副本减去正在进行中的待复制记录。追随者像一个普通的Kafka消费者一样,从他们的领导人那里批量提取记录。
Kafka记录哪些Kafka经纪人还活着。为了活着,Kafka经纪人必须使用ZooKeeper的心跳机制来维护一个ZooKeeper会话,并且必须让所有的追随者与领导者同步,而不会落后太多。
这个ZooKeeper会话和同步是被称为同步的代理生存所需要的。同步副本被称为ISR。每个领导者都跟踪一组“同步副本”。
如果ISR /追随者死亡,则落后,领导者将从ISR中移除追随者。落后于复制品在replica.lag.time.max.ms
时段之后不同步的时候 。
当所有ISR将消息应用到其日志时,消息被认为是“已提交”的。消费者只看到提交的消息。Kafka保证:只要至少有一个ISR,承诺的信息就不会丢失。
Kafka分区是一个复制的日志。复制日志是分布式数据系统原语。复制日志对于使用状态机来实现其他分布式系统很有用。一个复制的日志模型对有序的一系列值“达成一致”。
当一个领导人活着的时候,所有的追随者只需要复制他们领导的价值观和秩序。如果领导者死了,Kafka从同步的追随者中选择一个新的领导者。如果一个生产者被告知一个消息被提交,然后领导失败,那么新当选的领导者必须有这个提交的消息。
你有更多的ISR; 在领导失败的时候选举越多。
法定人数是所需的确认数量,以及必须与选举领导人进行比较的日志数量,以确保可用性重叠。大多数系统使用多数票,Kafka不使用简单的多数投票来提高可用性。
在Kafka,领导人的选择是基于完整的日志。如果我们有一个复制因子3,那么至少两个ISR必须在领导者声明发送的消息提交之前同步。如果一个新的领导者需要当选,不超过3次失败,新的领导者保证有所有承诺的信息。
在追随者中,必须至少有一个包含所有提交的消息的副本。大多数投票的问题法定人数是没有多少失败,有一个无法操作的群集。
Kafka为每个领导人维护一套情监侦。只有这一套ISR的成员才有资格领导选举。在所有ISR确认写入之前,生产者写入分区的内容不会被提交。只要ISR设置发生变化,ISR就会持续到ZooKeeper。只有属于ISR成员的副本才有资格当选领导。
ISR法定人数的这种风格允许生产者在没有大多数所有节点的情况下继续工作,但只有ISR多数票。ISR仲裁的这种风格也允许副本重新加入ISR集并且拥有其投票计数,但是在加入之前必须完全重新同步,即使副本在其崩溃期间丢失未刷新的数据也是如此。
Kafka关于数据丢失的保证只有在至少一个副本同步的情况下才有效。
如果所有正在复制分区领导者的追随者都立即死亡,那么数据丢失Kafka保证是无效的。如果分区的所有副本都关闭,则默认情况下,Kafka选择作为首领活动的第一个副本(不一定在ISR集合中)(config unclean.leader.election.enable = true是缺省值)。这种选择有利于可用性的一致性。
如果一致性比您的用例的可用性更重要,那么您可以设置配置,unclean.leader.election.enable=false
那么如果所有副本都停止运行一个分区,Kafka会等待第一个ISR成员(而不是第一个副本)活跃起来以选出新的领导者。
生产者可以通过设置acks(0),仅前导(1)或所有副本(-1)来选择耐久性。
acks = all是默认值。总而言之,当所有当前的同步复制品(ISR)都收到该消息时,便会发生这种情况。
您可以在一致性和可用性之间进行权衡。如果耐用性超过可用性,那么禁用不干净的领导者选举并指定最小的ISR大小。
最小的ISR规模越大,保证一致性就越好。但是,如果ISR集的大小小于最小阈值,则ISR的最小ISR越高,可用性就越低,因为分区不可用。
Kafka已经为消费者和生产者制定了限制他们被允许消费的带宽的限额。这些配额阻止消费者或生产者占用Kafka经纪人资源。配额是由客户端ID或用户。配额数据存储在ZooKeeper中,所以更改不需要重新启动Kafka代理。
使用配额限制消费者的带宽。
所有。这意味着所有ISR必须将消息写入其日志分区。
Kafka选择第一个复制品(不一定在ISR集合中),作为领导者活跃起来,unclean.leader.election.enable=true
以支持可用性。
通过线路以及磁盘优化IO吞吐量。它还通过压缩整个批次来提高压缩效率。
成为高吞吐量,可扩展的流媒体数据平台,用于对日志聚合,用户活动等大容量事件流进行实时分析。
生产者原子写入,性能改进和生产者不发送重复的消息。
有三种消息传递语义:最多一次,至少一次,恰好一次。
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。