22. Metrics:Spectator,Servo 和 Atlas

当一起使用时,Spectator/Servo 和 Atlas 提供近乎 real-time 操作的洞察平台。

Spectator 和 Servo 是 Netflix 的 metrics 集合 libraries。 Atlas 是一个 Netflix metrics 后端,用于管理维 time 系列数据。

Servo 服务 Netflix 已经有好几年了,但仍然可以使用,但逐渐被逐步淘汰,而 Spectator 只能用于 Java 8. Spring Cloud Netflix 提供对两者的支持,但鼓励基于 Java 8 的应用程序使用 Spectator 。

要启用 Servo,必须将spring.metrics.servo.enabled设置为true

22.1 维度与分层 Metrics

Spring Boot Actuator metrics 是分层的,metrics 只由 name 分隔。这些名称通常遵循命名约定,该约定将 key/value 属性对(维度)嵌入由句点分隔的 name 中。考虑两个 endpoints,root 和 star-star 的以下 metrics:

{
    "counter.status.200.root": 20,
    "counter.status.400.root": 3,
    "counter.status.200.star-star": 5,
}

第一个指标为我们提供了每单位 time 对根端点的成功请求的标准化计数。但是,如果系统有 20 个 endpoints 并且您希望获得针对所有 endpoints 的成功请求的计数,该怎么办?某些分层 metrics 后端允许您指定一个通配符,例如counter.status.200.*,它将读取所有 20 个 metrics 并聚合结果。或者,您可以提供HandlerInterceptorAdapter截取并记录所有成功请求的counter.status.200.all等度量标准,而不管端点如何,但现在您必须编写 20 个不同的 metrics。同样,如果您想知道服务中所有 endpoints 的成功请求总数,您可以指定一个通配符,例如counter.status.2*.*

即使存在对分层 metrics 后端的通配符支持,命名一致性也很困难。具体来说,name string 中这些标记的位置可能随着 time 而中断,从而破坏了查询。对于 example,假设我们为 HTTP 方法的上层次 metrics 添加了一个额外的维度。然后counter.status.200.root变成counter.status.200.method.get.root等。我们的counter.status.200.*突然不再具有相同的语义含义。此外,如果未在代码库中统一应用新维度,则某些查询可能变得不可能。这很快就会失控。

Netflix metrics 已标记(a.k.a .dimension)。每个度量标准都有一个 name,但是这个命名的度量标准可以包含多个统计信息和“标记”key/value 对,可以提供更多的查询灵活性。实际上,统计数据本身都记录在特殊标记中。

使用 Netflix Servo 或 Spectator 记录,上述根端点的计时器每个状态 code 包含 4 个统计信息,其中计数统计信息与 Spring Boot Actuator 的计数器相同。在到目前为止我们遇到 HTTP 200 和 400 的 event 中,将有 8 个可用数据点:

{
    "root(status=200,stastic=count)": 20,
    "root(status=200,stastic=max)": 0.7265630630000001,
    "root(status=200,stastic=totalOfSquares)": 0.04759702862580789,
    "root(status=200,stastic=totalTime)": 0.2093076914666667,
    "root(status=400,stastic=count)": 1,
    "root(status=400,stastic=max)": 0,
    "root(status=400,stastic=totalOfSquares)": 0,
    "root(status=400,stastic=totalTime)": 0,
}

22.2 默认 Metrics Collection

在没有任何其他依赖项或 configuration 的情况下,基于 Spring Cloud 的服务将自动配置 Servo MonitorRegistry并开始在每个 Spring MVC 请求上收集 metrics。默认情况下,将为每个 MVC 请求记录带 name rest的 Servo 计时器,该请求标记为:

  • HTTP 方法

  • HTTP 状态(e.g. 200,400,500)

  • URI(如果 URI 为空,则为“root”),为 Atlas 清理

  • exception class name,如果请求处理程序抛出 exception

  • 如果在请求中设置了 key 匹配netflix.metrics.rest.callerHeader的请求标头,则调用方。 netflix.metrics.rest.callerHeader没有默认的 key。如果您希望收集呼叫者信息,则必须将其添加到 application properties。

设置netflix.metrics.rest.metricName property 以将度量标准的 name 从rest更改为您提供的 name。

如果启用 Spring AOP 并且运行时 classpath 上存在org.aspectj:aspectjweaver,则 Spring Cloud 也将在使用RestTemplate进行的每个 client 调用上收集 metrics。将为每个 MVC 请求记录 name 为restclient的 Servo 计时器,该请求标记为:

  • HTTP 方法

  • HTTP 状态(e.g. 200,400,500),如果响应返回 null 则为“CLIENT_ERROR”,如果在执行RestTemplate方法期间发生IOException则为“IO_ERROR”

  • URI,为 Atlas 清理

  • 客户名称

避免在RestTemplate中使用硬编码的 url 参数。定位动态 endpoints 时,请使用 URL 变量。这将避免潜在的“GC Overhead Limit Reached”问题,其中ServoMonitorCache将每个 url 视为唯一的 key。

// recommended
String orderid = "1";
restTemplate.getForObject("http://testclient/orders/{orderid}", String.class, orderid)

// avoid
restTemplate.getForObject("http://testclient/orders/1", String.class)

22.3 Metrics Collection:Spectator

要启用 Spectator metrics,请在spring-boot-starter-spectator上包含依赖项:

<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-spectator</artifactId>
    </dependency>

在 Spectator 用语中,仪表是命名的,键入的和标记的 configuration,度量表示 time 中某个点的给定仪表的 value。 Spectator 计量表由注册表创建和控制,该注册表目前有几个不同的 implementations。 Spectator 提供 4 种类型:计数器,计时器,仪表和分布摘要。

Spring Cloud Spectator integration 为您配置一个可注入的com.netflix.spectator.api.Registry实例。具体来说,它在 order 中配置ServoRegistry实例,以统一 REST metrics 的集合,并将 metrics 导出到单个 Servo API 下的 Atlas 后端。实际上,这意味着您的 code 可以使用 Servo 监视器和 Spectator 计量器的混合,并且两者都将被 Spring Boot Actuator MetricReader实例挖出,并且两者都将被运送到 Atlas 后端。

22.3.1 Spectator Counter

计数器用于测量某些 event 发生的速率。

// create a counter with a name and a set of tags
Counter counter = registry.counter("counterName", "tagKey1", "tagValue1", ...);
counter.increment(); // increment when an event occurs
counter.increment(10); // increment by a discrete amount

计数器记录单个 time-normalized 统计信息。

22.3.2 Spectator Timer

计时器用于衡量一些 event 采取的方式。 Spring Cloud 会自动记录 Spring MVC 请求和有条件RestTemplate请求的计时器,这些请求稍后可用于创建与延迟等请求相关的 metrics 的仪表板:

图 1_.请求延迟

RequestLatency

// create a timer with a name and a set of tags
Timer timer = registry.timer("timerName", "tagKey1", "tagValue1", ...);

// execute an operation and time it at the same time
T result = timer.record(() -> fooReturnsT());

// alternatively, if you must manually record the time
Long start = System.nanoTime();
T result = fooReturnsT();
timer.record(System.nanoTime() - start, TimeUnit.NANOSECONDS);

计时器同时记录 4 个统计数据:count,max,totalOfSquares 和 totalTime。如果您在记录时间的每个 time 时在计数器上调用increment()一次,则计数统计数将始终匹配计数器提供的单个标准化值。因此,对于单个操作,很少需要单独计数和 time。

对于long running 操作,Spectator 提供了一个特殊的LongTaskTimer

22.3.3 Spectator Gauge

仪表用于确定一些当前的 value,例如队列的大小或 running state 中的线程数。由于对仪表进行了采样,因此它们不会提供有关这些值如何在 samples 之间波动的信息。

仪表的正常使用包括在初始化中使用 id 注册一次,对 object 进行采样的 reference,以及函数来获取或计算基于 object 的数值 value。 object 的 reference 单独传递,Spectator 注册表将保持对 object 的弱参考。如果 object 被垃圾收集,那么 Spectator 将自动删除注册。如果此 API 被滥用,请参阅 Spectator 文档中有关潜在 memory 泄漏的那个笔记

// the registry will automatically sample this gauge periodically
registry.gauge("gaugeName", pool, Pool::numberOfRunningThreads);

// manually sample a value in code at periodic intervals -- last resort!
registry.gauge("gaugeName", Arrays.asList("tagKey1", "tagValue1", ...), 1000);

22.3.4 Spectator 分布摘要

分发摘要用于跟踪 events 的分布。它类似于计时器,但更通用的是,大小不必是 time 的一段时间。例如,可以使用分发摘要来测量命中服务器的请求的有效负载大小。

// the registry will automatically sample this gauge periodically
DistributionSummary ds = registry.distributionSummary("dsName", "tagKey1", "tagValue1", ...);
ds.record(request.sizeInBytes());

22.4 Metrics Collection:Servo

如果您的 code 是在 Java 8 上编译的,请使用 Spectator 而不是 Servo,因为 Spectator 注定要在 long 术语中完全替换 Servo。

在 Servo 用语中,监视器是命名,类型化和标记的 configuration,度量标准表示 time 中某个点的给定监视器的 value。 Servo 监视器在逻辑上等同于 Spectator 计量器。 Servo 监视器由MonitorRegistry创建和控制。尽管有上述警告,Servo 确实有更广泛的 array监视器选项,而 Spectator 有米。

Spring Cloud integration 为您配置一个可注入的com.netflix.servo.MonitorRegistry实例。在 Servo 中创建适当的Monitor类型后,recording 数据的 process 与 Spectator 完全相似。

22.4.1 Creating Servo Monitors

如果您正在使用 Spring Cloud 提供的 Servo MonitorRegistry实例(特别是DefaultMonitorRegistry的实例),则 Servo 为检索计数器计时器提供了便利 classes。这些便利 classes 确保只为 name 和 tags 的每个唯一组合注册一个Monitor

要在 Servo 中手动创建 Monitor 类型,尤其是对于未提供便捷方法的更奇特的监视器类型,请通过提供MonitorConfig实例来实例化相应的类型:

MonitorConfig config = MonitorConfig.builder("timerName").withTag("tagKey1", "tagValue1").build();

// somewhere we should cache this Monitor by MonitorConfig
Timer timer = new BasicTimer(config);
monitorRegistry.register(timer);

22.5 Metrics 后端:Atlas

Atlas 是由 Netflix 开发的,用于管理维_系列数据,以获得近乎可操作的洞察力。 Atlas features in-memory 数据存储,允许它非常快速地收集和报告 metrics 的非常大的数。

Atlas 捕获运营情报。商业智能是收集用于分析整个时间趋势的数据,而运营情报则提供系统内当前正在发生的情况的图片。

Spring Cloud 提供了spring-cloud-starter-netflix-atlas,它具有您需要的所有依赖项。然后使用@EnableAtlas注释 Spring Boot application 并使用netflix.atlas.uri property 为 running Atlas 服务器提供一个位置。

22.5.1 Global 标签

Spring Cloud 使您可以为发送到 Atlas 后端的每个指标添加标签。 Global 标签可用于通过 application name,environment,region 等分隔 metrics。

实现AtlasTagProvider的每个 bean 都将对 global 标记列表做出贡献:

@Bean
AtlasTagProvider atlasCommonTags(
    @Value("${spring.application.name}") String appName) {
  return () -> Collections.singletonMap("app", appName);
}

22.5.2 使用 Atlas

要引导 in-memory 独立的 Atlas 实例:

$ curl -LO https://github.com/Netflix/atlas/releases/download/v1.4.2/atlas-1.4.2-standalone.jar
$ java -jar atlas-1.4.2-standalone.jar

在 r3.2xlarge(61GB RAM)上运行的 Atlas 独立节点可以在给定的 6 小时窗口内每分钟处理大约 200 万个 metrics。

一旦 running 并且您已经收集了一些 metrics,请通过在 Atlas 服务器上列出标签来验证您的设置是否正确:

$ curl http://ATLAS/api/v1/tags

在针对您的服务执行多个请求后,您可以通过在浏览器中粘贴以下 URL 来收集有关每个请求的请求延迟的一些非常基本的信息:http://ATLAS/api/v1/graph?q=name,rest,:eq,:avg

Atlas wiki 包含编译 sample 查询用于各种场景。

确保使用double 指数平滑检出提醒哲学和 docs 以生成动态警报阈值。

22.6 重试失败的请求

Spring Cloud Netflix 提供了多种方式来发出 HTTP 请求。您可以使用负载平衡的RestTemplate,Ribbon 或 Feign。无论您如何选择 HTTP 请求,请求都可能会失败。请求失败时,您可能希望自动重试请求。要在使用 Sping Cloud Netflix 时完成此操作,您需要在 application 的 classpath 中包含Spring 重试。当 Spring Retry 存在时,负载均衡RestTemplates, Feign 和 Zuul 将自动重试任何失败的请求(假设您 configuration 允许它)。

22.6.1 BackOff Policies

默认情况下,重试请求时不使用退避 policy。如果要配置退避 policy,则需要创建类型的 bean,它将用于为给定服务创建BackOffPolicy

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedBackOffPolicyFactory backOffPolciyFactory() {
        return new LoadBalancedBackOffPolicyFactory() {
            @Override
            public BackOffPolicy createBackOffPolicy(String service) {
                return new ExponentialBackOffPolicy();
            }
        };
    }
}

22.6.2 Configuration

无论何时 Ribbon 与 Spring 重试一起使用,您都可以通过配置某些 Ribbon properties 来控制重试功能。您可以使用的 properties 是client.ribbon.MaxAutoRetriesclient.ribbon.MaxAutoRetriesNextServerclient.ribbon.OkToRetryOnAllOperations。有关 properties 的作用的说明,请参阅Ribbon 文档

启用client.ribbon.OkToRetryOnAllOperations包括重新发送 POST 请求,这些请求会因请求主体的缓冲而对服务器资源产生影响。

此外,您可能希望在响应中返回某些状态代码时重试请求。您可以列出希望 Ribbon client 使用 property clientName.ribbon.retryableStatusCodes重试的响应代码。例如

clientName:
  ribbon:
    retryableStatusCodes: 404,502

您还可以创建LoadBalancedRetryPolicy类型的 bean 并实现retryableStatusCode方法以确定是否要在给定状态 code 的情况下重试请求。

22.6.3 Zuul

您可以通过将zuul.retryable设置为false来关闭 Zuul 的重试功能。您还可以通过将zuul.routes.routename.retryable设置为false来在 route 基础上禁用 route 上的重试功能。