原文地址
https://medium.com/hulu-tech-blog/how-hulu-uses-influxdb-and-kafka-to-scale-to-over-1-million-metrics-a-second-1721476aaff5
Hulu如何使用InfluxDB和Kafka每秒扩展到超过100万个指标
高级系统工程师Samir Jafferali
随着Hulu的持续增长,时间序列数据已成为我们在一段时间内监视各种信息的关键部分。这可以像机器性能指标或有关我们应用程序本身的数据一样简单。由于我们拥有的数据量很大,因此创建一种能够以冗余,可支持和可扩展的方式处理增长的体系结构至关重要。
为什么时间序列数据很重要?
时间序列数据使我们能够评估趋势,以便发现问题并采取措施。
下图最近用于确定内存泄漏,该泄漏会影响在特定数据中心运行的应用程序版本。
最初,每个开发团队都有自己的时间序列数据解决方案。由于大多数团队都有类似的需求,因此这是不一致且浪费资源的。为了解决这个问题,我们构建了原始的时间序列数据管道,该管道为整个工程组织的团队提供了时间序列数据库。
该管道基于Graphite,并将数据存储到OpenTSDB。在高峰时,该石墨集群的吞吐量为每秒140万个指标。在维持这一流程的同时,我们遇到了许多问题,随着我们不断扩展规模,这些问题变得越来越普遍。
其中许多问题源于为Hulu所有开发团队提供管道共享服务。由于所需的巨大吞吐量和我们需要支持的基数,其他问题是可伸缩性的固有问题。
初始管道遇到的挑战
用户经常且无意间会在其度量标准名称空间中发送唯一数据,例如时间戳或另一个唯一标识符。其中一些指标的示例包括:
stats.application.dc1.1557471341.count 1 1557471341
stats.application.dc1.1557471345.count 1 1557471345
stats.application.dc1.1557471346.count 1 1557471346
这导致了我们命名空间中基数的爆炸式增长,这影响了接收速度并导致了整个管道的稳定性问题。尽管有可能阻止这些行为,但这样做非常困难,因为它需要添加一个与问题度量匹配的条件,而又不影响合法度量。
由于吞吐量的限制,我们在创建规则的复杂性和数量方面受到限制,因为必须针对收到的每个指标评估每个规则。这还需要以滚动方式在处理我们的指标数据涉及的许多节点上完成。因此,很少有条件添加条件来阻止这些有问题的指标,而倾向于让应用程序停止发送指标。不幸的是,在大多数情况下,花费的时间导致数据丢失。
检索数据时,许多用户会无意中运行长时间或占用大量资源的查询,这最终将导致为其提供服务的后端超时并脱机。没有停止这种行为的限制,这也影响了整个石墨的稳定性。
发送的指标也没有责任。由于格式不是标准化的,因此找出哪个服务正在发送特定指标需要大量的猜测,而在我们实际需要这样做时非常困难。
最后,由于我们的设置,所有指标都被发送到了两个数据中心之一。如果发生故障,则整个数据中心的指标将无法访问。此外,由于我们只有一个统一的接口来检索这些指标,因此我们优先于一个数据中心。这是有问题的,因为如果用户向第一个优先级数据中心发送了一个度量标准,但随后决定使用另一个度量标准,则由于第一个名称空间中已经存在命名空间,因此将无法访问他们的新度量标准,从而导致很多混乱。
InfluxDB-初始架构
为了解决这些问题,我们决定基于InfluxDB从头开始重新构建统计数据管道。对于我们的第一次尝试,我们创建了两个集群,在我们的两个主要数据中心(DC1和DC2)中每个都有一个。这两个群集将包含相同的数据。
在此基础上构建了一个度量标准中继群集。所有度量标准都将发送到中继群集。这些集群的唯一目的是将收到的所有指标推送到我们的两个InfluxDB集群。这样就可以从任何数据中心检索所有指标,从而完全消除了我们以前在Graphite体系结构中遇到的指标可用性问题。我们在每个数据中心中创建了这一层。
在此度量标准中继层上,我们还实现了必需的标签,这是Hulu中每个应用程序唯一的标识符。这使我们可以轻松地追溯每个度量标准的来源。没有此必需标签的所有指标都将被完全删除。
Hulu的所有机器都运行Telegraf守护程序(https://github.com/influxdata/telegraf)。我们已将该守护程序配置为报告所有计算机统计信息,并且还监听localhost上的指标。我们鼓励所有开发人员将指标发送到localhost,因为我们已将Telegraf配置为自动为其接收的所有指标添加标准标签。这些标签包括原始数据中心,计算机名称和计算机ID。
此设置效果很好。我们测试了每秒超过200万个指标的吞吐量,没有任何问题。但是,我们很快遇到了一个问题,导致我们重新评估了当前设置。
具体来说,我们的一个集群在一段时间内不可用,导致所有指标仅被推送到单个(在线)数据中心,然后在被复制到另一个集群之前被丢弃。一旦有问题的群集再次可用,数据就会出现差异,需要手动进行重新同步群集。我们意识到,我们需要一种方法来更优雅地解决此类问题,并减少人工干预。
InfluxDB —更改的体系结构
我们在设计中创建了两个新层,包括每个数据中心中的Kafka队列,以及一个InfluxDB writer层以解决此问题。
指标仍会发送到原始中继集群,但不再从那里直接路由到我们的InfluxDB集群。而是将它们发送到接收它们的数据中心中的本地Kafka队列。
InfluxDB编写器层是在InfluxDB群集所在的每个数据中心中创建的。该层的唯一目的是连接到我们所有数据中心中的每个Kafka队列,并将它们写入其本地InfluxDB集群。如果其中一个InfluxDB集群发生故障,则该数据中心中的“写入器”将停止写入该集群,但另一个数据中心将继续提取其指标。一旦出现问题的群集重新联机后,该数据中心中的“编写者”就会从他们停下来的地方开始,写自己的群集。一旦被捕获,两个群集将再次处于一致状态。
这种设计还使我们能够完全禁用基础结构的大部分(甚至整个数据中心),并将它们路由到另一个数据中心,而对最终用户没有任何影响。
此设计解决了我们的许多问题,但是,有两个问题没有解决。
查询限制
我们仍然遇到用户长时间运行查询或问题查询的问题。用户有时会进行很长时间的查询(通常是偶然的),这会导致其他用户的性能下降。因此,我们为此目的创建了一个新的微服务Influx Imposer。
该应用程序登录到我们的两个InfluxDB集群,并每分钟检查一次正在运行的查询。如果超过某些阈值(例如60秒)或是危险/资源密集型查询,则将其杀死。我们还实现了终止查询的日志记录,以用于调试目的,并且在实现后已经看到了大大提高的稳定性。
阻止/过滤“不良”指标
用户可能仍会在名称空间中发送带有时间戳的度量标准,或者发送一些可能导致数据集基数非常大的独特数据。为了解决此特定问题,我们创建了另一个微服务,称为Barricade。
Barricade是数据库支持的API,其中包含动态黑名单。任何时候需要通过标签,度量或几乎任何其他信息将特定指标列入黑名单时,都会将其添加到此数据库中。到达该位置后,更改将传播到InfluxDB编写器计算机上的所有本地配置。这些机器不断轮询路障以获取其更新的黑名单规则。如果检测到更改,则“编写器”将重新生成其本地备用列表配置。之所以在InfluxDB编写器层执行此操作,是因为该层上的任何更改都不会导致度量获取的中断,因此添加新的黑名单条目是没有影响的。
时间序列数据现在并将继续成为Hulu评估趋势并对之做出反应的能力的重要组成部分。我们能够解决我们以前的流程中的所有问题,现在正在将所有用户移出我们的旧平台。在撰写本文时,我们的新统计信息流每秒处理超过一百万个指标,并且每天都在增长。