在本系列中不要错过第二部分:KAFKA主题分区的有效策略

New Relic是早期的采用者Apache Kafka.;我们早期认识到流行的分布式流平台可以是构建可扩展,高吞吐量实时流系统的伟大工具。我们建立了有很多关于如何有效传播处理负荷以获得最大可扩展性的经验。

Events管道团队负责挖掘New Relic的一些核心数据流——具体来说,事件数据。“这些是监测在特定时刻记录单个事件的数据的细粒度块。例如,事件可以是应用程序抛出的错误,浏览器上的页面视图,或电子商务购物车事务。

在这篇文章中,我们将展示如何构建我们的Kafka管道,将微服务缝合在一起,作为一个更新日志和“持久缓存”,所有的想法都是在我们的规模下尽可能平滑和有效地处理数据流。在接下来的文章中,我们将分享如何在这个管道中管理主题分区的想法。

这篇文章令人熟悉卡夫卡的基本知识,包括用户组、分区和抵消。所有的示例都是使用消费者和生产者api构建的。(请注意,这篇文章不是关于管理或故障排除Kafka集群。为此,看到kafkapocalypse:监控Kafka而不失去你的思想。)

缝合微野马服务

New Relic运营的微服务种类相当可观,由40多个敏捷工程团队管理,所有团队都相互依赖。因为它的可伸缩性、持久性和快速的性能,我们发现Kafka是在不同服务之间移动数据的好方法。通过主题传递消息的异步特性简化了服务的解耦,减少了一个服务中的更改或问题对另一个服务的影响。我们使用它对消息进行排队,在许多应用程序实例之间并行工作,并将消息广播到所有实例(我将在后面讨论)。

我们有一系列流处理服务,每个流处理服务在一个单独的容器中运行,在串联的事件数据上运行。我们通过Kafka主题将这些服务连接在一起;一项服务将消息生成到下一个服务的主题上以消耗并用作输入。例如,下图是我们运行的系统的简化,用于处理事件数据上的正在进行的查询:

流处理服务图

在源主题上流入的批次事件并被解析为单个事件。然后,它们与系统中的任何现有查询匹配。最后一步将事件聚合到时间窗口,将查询结果输出到结果主题。注意我们使用活动时间-在事件发生时进行处理-确定窗口成员。

由于与Kafka一起拼接这些服务允许解耦,一个服务的问题不会导致其他服务的问题。我们还能够根据流量卷动态部署每个应用程序的更多或更少实例。我们可以让Kafka自动重新平衡负荷。

我们将事件驱动的进程(解析和匹配)与基于时间的过程(聚合)分开。事件驱动的过程很容易理解,但随着世代的编程师已经了解到,时间通常很难处理。这就是为什么我们将棘手的时间逻辑分离为自己的服务。

使用Kafka作为一个更新日志

更改内容是包含在旨在从开始完成读取的主题上的数据,很可能在应用程序启动时。目标是通过重播历史来重建国家。该服务可能会继续侦听更新。每个消费者都有自己的(或没有)组并重新开始最早抵消。

那么,回到事件数据查询系统的示例。我们的查询API生成一个查询主题。每个服务订阅(消费)查询主题。当一个服务启动时,它使用来自最早偏移,因此通过整个主题读取并继续以这种方式获得更新。这统一了启动和侦听过程,因为消费者和处理程序可以在服务的一生中以相同的方式运行。

通常,这是轮询数据库的一个有用的替代方案。

在这种情况下,您有两种主题保留选项。第一个选项是将主题保留时间设置为足够长的时间,以捕获重建状态所需的所有消息。另一种选择是使用日志压实。这无限期地保留了每个键的最新副本。在任何一种情况下,请记住,该主题将有多少条消息,以及在启动时重建其状态时,您的服务可能需要快速阅读整个主题。

在我们的查询API中,所有注册到系统中的查询都有一个最大生存时间(TTL)1小时。因此,我们知道,所有查询要么被保留一小时,要么被手动取消,要么用放置在查询主题上的新消息进行更新。当我们第一次开发这个系统时,我们慷慨地将主题保留时间设定为两个小时。但我们发现,使用该主题的应用程序启动速度变慢,花了几分钟来处理查询主题,而我们原本预计只需要几秒钟。结果是,我们未能调整日志段的大小(默认为一周)来匹配保留期。为了解决这个问题,我们设置段.ms.匹配保留,并观看了我们的主题消耗时间走下去。

随着系统的发展,我们正在寻找摆脱查询TTL。如果发生这种情况,则将使用日志压缩而不是时间保留。

Kafka作为国家缓存

我们使用Kafka主题来存储和重新加载已经快照的状态。这就是我所谓的“持久缓存”,因为没有更好的术语,这意味着使用Kafka主题来存储和重新加载快照状态。

这是它的工作原理:

我们假设我们还可以从Kafka主题获取其输入数据,经常将当前状态生成“快照”主题。此“快照”主题具有1:1匹配的分区,以“主要”消耗主题。换句话说,如果应用程序实例在某些主题上从分区2消耗和处理数据,则实例将在“快照”主题上为分区2生成该状态的快照。一般来说,“快照”主题只需要短暂的保留,并且可以产生的数据比消耗更多。

当一个实例启动时,它从“main”消耗的主题中获取赋值。然后,它使用静态分区分配从相同的分区读取整个快照主题。在主主题上恢复消费的起始偏移量来自快照中保存的元数据。

要返回到我们的聚合器服务的示例,该服务在发布结果之前会构建几分钟的状态。这是一个高吞吐量的实时系统,因此在服务重新启动时备份ingest主题中几分钟的数据是不可行的。因此,我们开始将快照状态保存到一个“快照”主题,该主题镜像了分区计数中的主摄取主题。我们还生成到“快照”主题的发布消息,以确定何时忽略快照。我们保存与快照本身中的数据相关联的ingest主题的最新偏移量,并使用它计算出ingest主题的起始点。

聚合器服务快照管理

例如,如果我们每个摄取主题分区有一个实例,并且在启动时将匹配事件主题的实例分配分区7,它将读取快照主题的分区7的所有快照。它将使用快照元数据来确定从匹配的事件主题中开始读取的起始偏移,并从匹配事件主题和处理数据的分区7开始消耗。然后,它将保存新的快照和发布标记,以持续的方式分区快照主题。结果将发布到结果主题的适当(和不相关的)分区。

人们可能认为日志压缩在这里有用。但是,仍然需要手动取消复制,因为日志压缩无法瞬间工作。日志Concumation仅以间隔运行,仅在完成的日志段上运行。因此,要可靠地重建状态,需要重复数据以确保仅使用最新的快照。

请注意,我们考虑了用于存储我们快照的其他数据库或缓存选项,但我们决定使用Kafka,因为它会降低我们的依赖项,它相对于其他选项的复杂性更为复杂,而且它很快。

流处理和并发性

我们发现了干扰模式,特别是LMAX粉碎机图书馆对于高吞吐量的Kafka服务来说是非常有用和互补的。

disruptor类似于异步阻塞队列,由一个循环数组来备份,该数组将对象分发或多播给worker线程。关于吞吐量和延迟,它有一个很好的故事。它在缓冲区环上预先分配对象并重用它们,从而节省了垃圾收集的时间。

除了获得应用程序并发的有效机制外,该策略还具有仅需要以单线程方式对每个处理程序进行推理的极大优势。

在我们所有的服务中,我们使用Distruptor模式并将处理数据与一个或多个消费的分区并行化。我们将其施加到最需要的并发,这对于我们通常意味着将消息扇动到处理程序线程,并与业务逻辑处理一起解压缩和反序列化。

我们还使用Distruptor Handler同时更新状态。我们将消费者与不同主题的消费者融为一体,以以线程安全的方式操纵共享状态。我们在所有应用程序中执行此操作,下图描绘了聚合器服务的高级结构。

聚合器扰乱者服务

除了Kafka消息外,我们还通过Distruptor通过编程生成的控制消息,例如Timing Ticks,告诉处理程序线程检查聚合以完成和发布。我们在这些不同的数据源周围不需要额外的锁定/同步机制;处理程序线程将一次只读任何类型的一条消息。

可扩展性是一个持续的过程

在Kafka周围建造了很多伟大且引人注目的流媒体系统。随着您的业务需求规模,具有有效传播处理负荷的策略是增加可扩展性的重要组成部分。此外,还原依赖性和复杂性可以有助于提高代码可维护性和可靠性。新的遗物的事件管道已经走了很长的路,而随着我们继续增长,我们将继续找到新的方式来充分利用Kafka。

阅读第二部分:KAFKA主题分区的有效策略

Amy Boyle是新遗物的高级软件工程师,在核心数据平台上工作。她的兴趣包括分布式系统,可读代码和小狗。查看帖子

对新遗物博客的写作有兴趣吗?亚搏体育登入网给我们发一份建议书!