Skip to content

Latest commit

 

History

History
executable file
·
132 lines (76 loc) · 10.1 KB

Instrumentation.md

File metadata and controls

executable file
·
132 lines (76 loc) · 10.1 KB

instrumentation


此页面提供了一套固定的指导方针,用于检测代码。

How to instrument

简短的回答是衡量一切。 每个库,子系统和服务都应至少有一些指标,以便您大致了解它的执行情况。

仪器应该是代码中不可或缺的一部分。 在您使用它们的同一文件中实例化度量标准类。 当您追踪错误时,这使得从警报到控制台变得容易编码。

三种类型服务

出于监控目的,服务通常可以分为三种类型:在线服务,离线处理和批处理作业。 它们之间存在重叠,但每种服务都倾向于适合这些类别之一。

在线服务系统

在线服务系统是人或其他系统期望立即响应的系统。 例如,大多数数据库和HTTP请求属于此类别。

此类系统中的关键指标是执行的查询数,错误数和延迟。 正在进行的请求数量也很有用。

有关计算失败的查询,请参阅下面的“失败”部分

应在客户端和服务器端监视在线服务系统。 如果双方看到不同的行为,那么这是非常有用的调试信息。 如果服务有很多客户端,那么服务单独跟踪它们也是不切实际的,所以他们必须依赖自己的统计数据。

在查询开始时或结束时是否计算是一致的。 当它们结束时建议,因为它将与错误和延迟统计对齐,并且往往更容易编码。

线下处理

对于离线处理,没有人正在积极等待响应,并且批量工作很常见。 可能还有多个处理阶段。

对于每个阶段,跟踪进入的项目,正在进行的项目,上次处理的项目以及发送的项目数量。 如果批处理,您还应该跟踪进出的批次。

知道系统最后一次处理某些东西对于检测它是否已经停止是有用的,但它是非常本地化的信息。 更好的方法是通过系统发送心跳:一些虚拟项目一直通过并包含插入时的时间戳。 每个阶段都可以导出它看到的最新心跳时间戳,让您知道项目在系统中传播的时间。 对于没有静默期且未进行处理的系统,可能不需要显式心跳。

批量任务

离线处理和批处理作业之间存在模糊界限,因为离线处理可以在批处理作业中完成。批处理作业的特征在于它们不会连续运行,这使得难以进行拼写。

批处理作业的关键指标是最后一次成功。跟踪作业的每个主要阶段所花费的时间,总体运行时间以及作业完成的最后时间(成功或失败)也很有用。这些都是仪表,应该推到PushGateway。通常还有一些特定于工作的整体统计数据可用于跟踪,例如处理的记录总数。

对于运行时间超过几分钟的批处理作业,使用基于拉式的监控来抓取它们也很有用。这使您可以跟踪与其他类型的作业相同的度量标准,例如与其他系统通信时的资源使用和延迟。如果作业开始变慢,这可以帮助调试。

对于经常运行的批处理作业(例如,通常比每15分钟更多),您应该考虑将它们转换为守护程序并将它们作为脱机处理作业处理。

子系统

除了三种主要类型的服务之外,系统还有应该监控的子部分。

Libraries库

库应该提供检测,而不需要用户进行其他配置。

如果它是用于访问进程外部某些资源的库(例如,网络,磁盘或IPC),则跟踪整体查询计数,错误(如果可能存在错误)和延迟至少。

根据库的重量,跟踪库本身内部的错误和延迟,以及您认为可能有用的任何常规统计信息。

应用程序的多个独立部分可以针对不同的资源使用库,因此在适当的情况下注意区分用途和标签。 例如,数据库连接池应该区分它正在与之通信的数据库,而不需要区分DNS客户端库的用户。

日志

作为一般规则,对于每行日志记录代码,您还应该有一个递增的计数器。 如果您发现有趣的日志消息,您希望能够查看它发生的频率和持续时间。

如果在同一个函数中存在多个密切相关的日志消息(例如,if或switch语句的不同分支),则有时可以为所有这些消息增加一个计数器。

导出整个应用程序记录的信息/错误/警告行总数通常也很有用,并在发布过程中检查重大差异。

失败

应该像记录一样处理故障。 每次出现故障时,都应增加计数器。 与日志记录不同,根据代码的结构,错误也可能会冒泡到更通用的错误计数器。

报告失败时,通常应该有一些其他指标代表总尝试次数。 这使得故障率易于计算。

线程池 TreadPools

对于任何类型的线程池,关键指标是排队请求的数量,正在使用的线程数,线程总数,处理的任务数以及它们花费的时间。 跟踪队列中等待的时间也很有用。

Caches缓存

缓存的关键指标是总查询,命中,总体延迟,然后是缓存所在的任何在线服务系统的查询计数,错误和延迟。

Collectors收集器

在实现一个非平凡的自定义指标收集器时,建议导出一个指标,表示收集在几秒钟内所花费的时间以及另一个收集的错误数量。

这是可以将持续时间导出为量表而不是汇总或直方图的两种情况之一,另一种是批量作业持续时间。 这是因为两者都代表关于特定push/scrape的信息,而不是随时间跟踪多个持续时间。

关注的事情

在进行监控时,还有一些需要注意的事项,特别是Prometheus特定的事项。

使用标签

很少有监控系统具有标签和表达语言的概念来利用它们,因此需要一些习惯。

当你有想要的add/average/sum的多个度量指标时,它们通常应该是一个包含标签而非多个指标的指标。

例如,不是http_responses_500_totalhttp_responses_403_total,而是创建一个名为http_responses_total的度量标准,其中包含HTTP响应代码的代码标签。 然后,您可以将整个指标作为规则和图形中的一个处理。

根据经验,度量名称的任何部分都不应该在程序上生成(而是使用标签)。 一个例外是从另一个监视/检测系统代理度量。

另请参阅命名部分。

不要过度使用标签

每个标签集都是一个额外的时间序列,具有RAM,CPU,磁盘和网络成本。通常开销可以忽略不计,但在具有大量指标和数百个服务器上的数百个标签集的情况下,这可以快速增加。

作为一般准则,请尽量将指标的基数保持在10以下,对于超过该指标的指标,我们的目标是将其限制在整个系统中。绝大多数指标都没有标签。

如果您有一个基数超过100的指标或可能增长的指标,请研究其他解决方案,例如减少维数或将分析从监控转移到通用处理系统。

为了让您更好地了解底层数字,让我们看一下node_exporter。 node_exporter公开每个已安装文件系统的度量标准。每个节点都有几十个时间序列,例如node_filesystem_avail。如果你有10,000个节点,你最终会得到大约100,000个node_filesystem_avail的时间序列,这对Prometheus来说很好。

如果您现在要为每个用户添加配额,您将很快达到10,000个节点上10,000个用户的两位数百万。这对Prometheus目前的实施来说太过分了。即使数量较少,也存在机会成本,因为您无法在此计算机上拥有其他可能更有用的指标。

如果您不确定,请在没有标签的情况下开始,并随着具体使用情况的出现添加更多标签。

Counter vs. gauge, summary vs. histogram

了解用于给定度量的四种主要度量标准类型中的哪一种非常重要。

要在countergauge之间进行选择,有一个简单的经验法则:如果值可以下降,那么它就是一个gauge

counter只能上升(并重置,例如进程重启时)。 它们可用于累积事件数量或每个事件的数量。 例如,HTTP请求的总数,或HTTP请求中发送的总字节数。 原始计数器很少有用。 使用rate()函数可以获得它们增加的每秒速率。

gauge可以设置,上升和下降。 它们对状态快照很有用,例如正在进行的请求,空闲/总内存或温度。 你永远不应该拿一个表的rate()

Summaryhistogram是他们自己部分讨论的更复杂的度量类型。

时间戳Timestamps, not time since

如果你想跟踪事件发生的时间,可以导出它发生的Unix时间戳 - 而不是它发生的时间。

通过导出时间戳,您可以使用表达式time() - my_timestamp_metric来计算自事件以来的时间,无需更新逻辑并保护您免受更新逻辑卡住。

内部循环 Inner loops

一般而言,仪器的额外资源成本远远超过它为运营和开发带来的好处。

对于性能至关重要的代码或在给定进程内每秒调用超过100k次的代码,您可能希望关注更新的指标数量。

Java counter需要12-17ns才能增加,具体取决于竞争条件。 其他语言也会有类似的表现。 如果内部循环的时间量很重要,请限制在内部循环中增加的度量标准数量并尽可能避免使用标签(或者缓存标签查找的结果,例如,Go中的With()返回值或者Java中的labels())。

还要注意涉及时间或持续时间的度量标准更新,因为获取时间可能涉及系统调用。 与涉及性能关键代码的所有事项一样,基准测试是确定任何给定代码影响的最佳方法

避免丢失的度量指标

在事情发生之前不存在的时间序列很难处理,因为通常的简单操作不再足以正确处理它们。 为了避免这种情况,对于您可能事先知道的任何时间序列,导出0(或NaN,如果0会产生误导)。

大多数Prometheus客户端库(包括GoJavaPython)将自动为您导出0以获取没有标签的指标。