Skip to main content

20小节:深入剖析Metrics监控中的那些坑(扩展篇)

作者:程序员马丁

在线博客:https://nageoffer.com

note

热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。

前言

这是一篇扩展文章,起因是星球里有位同学提出了一个很有意思的问题,我们先来看看。

这是我们之前的代码实现,这位同学认为在 Metrics.gauge 注册指标时,应该传入被缓存的 existingRuntimeInfo,而不是直接使用 runtimeInfo

private void micrometerMonitor(ThreadPoolRuntimeInfo runtimeInfo) {
String threadPoolId = runtimeInfo.getThreadPoolId();
ThreadPoolRuntimeInfo existingRuntimeInfo = micrometerMonitorCache.get(threadPoolId);
if (existingRuntimeInfo != null) {
BeanUtil.copyProperties(runtimeInfo, existingRuntimeInfo);
} else {
micrometerMonitorCache.put(threadPoolId, runtimeInfo);
}

Iterable<Tag> tags = CollectionUtil.newArrayList(
Tag.of(DYNAMIC_THREAD_POOL_ID_TAG, threadPoolId),
Tag.of(APPLICATION_NAME_TAG, ApplicationProperties.getApplicationName()));

Metrics.gauge(metricName("core.size"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getCorePoolSize);
Metrics.gauge(metricName("maximum.size"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getMaximumPoolSize);
Metrics.gauge(metricName("current.size"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getCurrentPoolSize);
Metrics.gauge(metricName("largest.size"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getLargestPoolSize);
Metrics.gauge(metricName("active.size"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getActivePoolSize);
Metrics.gauge(metricName("queue.size"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getWorkQueueSize);
Metrics.gauge(metricName("queue.capacity"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getWorkQueueCapacity);
Metrics.gauge(metricName("queue.remaining.capacity"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getWorkQueueRemainingCapacity);
Metrics.gauge(metricName("completed.task.count"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getCompletedTaskCount);
Metrics.gauge(metricName("reject.count"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getRejectCount);
}

这篇文章就围绕这个问题,和大家深入探讨一下 Metrics 的注册机制,以及基于这个机制我们如何进行代码重构优化。

Metrics 注册原理

1. 基于首次注册对象引用获取指标

我们先来看看这行代码的实际作用:

Metrics.gauge(metricName("xxx"), tags, runtimeInfo, ThreadPoolRuntimeInfo::getXxx);

这行代码背后的执行逻辑是这样的:

  1. 如果这个 metric(包括名字 + tag)是第一次注册,Micrometer 会绑定 runtimeInfo 对象的引用

  2. 如果 metric 已经注册过了,这次调用不会更新引用,只是返回已存在的 Gauge(Micrometer 内部维护了一个缓存结构);

  3. 因此,即使后续你传入了新的 runtimeInfo 对象,但这个新对象并不会被绑定,Micrometer 仍然会读取最初绑定的老对象的引用值

让我们看看 Micrometer 的 Metrics.gauge(...) 源码逻辑(以 SimpleMeterRegistry 为例):

解锁付费内容,👉 戳