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_.请求延迟
// 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.MaxAutoRetries
,client.ribbon.MaxAutoRetriesNextServer
和client.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 上的重试功能。