| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – dubbo-metrics 指标模块源码浅析</title><link>https://dubbo.apache.org/zh-cn/blog/java/codeanalysis/metrics/</link><description>Recent content in dubbo-metrics 指标模块源码浅析 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><atom:link href="https://dubbo.apache.org/zh-cn/blog/java/codeanalysis/metrics/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: 引言</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/%E5%BC%95%E8%A8%80/</guid><description> |
| <h2 id="引言">引言</h2> |
| <p>服务指标统计体系是 Dubbo 可观测能力的重要组成部分。 |
| dubbo-metrics 指标模块旨在将 dubbo 内部零散的 Metrics 相关类综合到一个单独的模块中,提供一套更加完善、全面、可拓展、解耦合的指标采样-统计-导出解决方案。</p> |
| <p>dubbo-metrics 模块包括:</p> |
| <ul> |
| <li>dubbo-metrics-api 公用接口包</li> |
| <li>dubbo-metrics-prometheus 普罗米修斯适配包</li> |
| <li>dubbo-metrics-metadata 元数据中心指标监控包</li> |
| <li>dubbo-metrics-registry 注册中心指标监控包</li> |
| <li>dubbo-metrics-config-center 配置中心指标监控包</li> |
| <li>dubbo-metrics-default 接口默认实现包,提供dubbo内部核心指标的监控功能</li> |
| </ul> |
| <p>在设计上,dubbo-metrics 深入应用事件驱动编程思想,总体体现出下图的事件处理链路:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/metrics-event-struct.png" alt="metrics-event-struct"></p> |
| <p>在拓展上,dubbo-metrics 抽象了一套指标导出接口与抽象实现,可实现兼容多种指标统计监控中心,默认提供了普罗米修斯实现。</p></description></item><item><title>Blog: 1-指标样本的收集与存储</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/1-%E6%8C%87%E6%A0%87%E6%A0%B7%E6%9C%AC%E7%9A%84%E6%94%B6%E9%9B%86%E4%B8%8E%E5%AD%98%E5%82%A8/</guid><description> |
| <h2 id="一指标样本的收集与存储">一、指标样本的收集与存储</h2> |
| <h3 id="指标样本收集">指标样本收集</h3> |
| <p><strong>指标收集器(Collector)是指标对外导出的入口</strong>。最终导出到指标统计中心的指标采样实际均直接来源于各个指标采样器。因此,我们将从各个收集器实现开始,分析 dubbo-metrics 模块是如何工作的。</p> |
| <p>指标收集操作定义在 MetricsCollector (指标采集器,SPI)接口中,可以通过它的实现收集某一类的指标样本(MetricSample)。它主要有以下实现,对应着不同类型的指标:</p> |
| <ul> |
| <li>ConfigCenterMetricsCollector <strong>配置中心操作相关指标收集器</strong> ,收集配置信息的变化次数</li> |
| <li>MetadataMetricsCollector <strong>元数据中心操作相关指标收集器</strong>,收集提供者、消费者对元数据中心操作(推送数据、拉取数据)情况的计数、耗时统计。</li> |
| <li>RegistryMetricsCollector <strong>注册中心相关操作指标收集器</strong>,收集应用级、接口级服务注册成功、失败、耗时的相关计数。</li> |
| <li>DefaultMetricsCollector <strong>默认指标收集器</strong>,内置多种采样器来完成不同类型的内部指标采样。</li> |
| <li>HistogramMetricsCollector <strong>直方图指标收集器</strong>,利用 micrometer API 处理直方图类型的指标,它的实现较为特殊。</li> |
| </ul> |
| <p>配置中心 、元数据、服务注册及默认指标收集器均实现自混合指标收集器(CombMetricsCollector)。混合指标收集器实现了 ApplicationMetricsCollector 、ServiceMetricsCollector 、MethodMetricsCollector 三个接口(定义按应用名收集、按应用名-服务名收集和按应用-方法名收集指标的操作),因此它们可以进行应用、服务和方法三个层面的指标收集工作。</p> |
| <p>默认指标收集器的特点是通过内部的指标采样器(MetricsSampler)完成指标事件的处理操作,而不是其它收集器的指标监听器(MetricsListener)</p> |
| <p>直方图指标收集器则负责收集直方图类型的指标。它利用直方图度量寄存器(HistogramMetricRegister)借助 micrometer API 完成直方图样本的采集。直方图类型包括百分位数、服务水平目标、最小预期值、最大预期值、统计数据分布有效期等。</p> |
| <p><strong>Collector的继承关系:</strong></p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/collectors.png" alt="collectors"></p> |
| <p>可以看出,每个指标收集器都具有来自 MetricsListener 的监听指标事件的能力。为什么指标收集器本身需要监听指标事件? 在后文中,我们将讨论指标收集器是如何利用内置的子转发器(SubDispatcher)转发指标事件,并完成计数处理的。</p> |
| <h3 id="指标样本存储">指标样本存储</h3> |
| <p>前文中,我们了解了指标收集的入口是指标收集器(Collector)。那么各个收集器从哪里收集指标样本?</p> |
| <p><strong>对于配置中心、元数据中心、 注册中心的指标收集器:</strong></p> |
| <p>它们分别负责采集三大中心模块的指标,均继承于<strong>混合数据收集器(CombMetricsCollector)</strong>,而混合数据收集器中实现了 export 方法 。</p> |
| <p>混合数据收集器内部有一个<strong>基本数据聚合器(BaseStatComposite)</strong>,它实现了 MetricsExport 接口,该接口定义了指标导出操作,混合数据收集器则利用它的 export 方法导出指标。</p> |
| <p>基本数据聚合器是一个抽象类,内有三个属性:ApplicationStatComposite 、ServiceStatComposite 和 RtStatComposite 。它们的作用:</p> |
| <ul> |
| <li><strong>ApplicationStatComposite 应用数据聚合器</strong>,应用程序级别相关事件的计数 ,根据指标Key( MetricsKey )和应用名统计指标,提供计数递增操作</li> |
| <li><strong>ServiceStatComposite 服务数据聚合器</strong>,服务级别相关事件的计数,根据指标Key、应用名和服务名统计指标,提供计数递增操作</li> |
| <li><strong>MethodStatComposite 方法数据聚合器</strong>,方法级别相关事件的计数,存储各方法RPC调用相关计数。</li> |
| <li><strong>RtStatComposite,Rt(Response Time,响应时间)数据聚合器</strong>,包括应用级别和服务级别。根据应用名、服务名、注册的指标名及相应相应时间统计指标,提供添加操作。</li> |
| </ul> |
| <p>对于以上四个聚合器,他们的职责就是存储某一类型的采样样本。</p> |
| <p>** 基本数据聚合器 (BaseStatComposite)** 对这三个子聚合器的操作进行了简单整合,统一提供给外界。<strong>而混合指标收集器(CombMetricsCollector)</strong> 也基本保留了内部基本数据聚合器的所有操作,将其封装为 <code>increment</code>、<code>setNum</code>、<code>addRt </code>三个方法(及它们的重载,分别收集应用级数据和服务级数据)向上提供。外部组件可以直接调用这些收集器完成指标更新操作。</p> |
| <p><strong>当调用元数据中心指标收集器、注册中心指标收集器的 collect 方法时,最终会调用<code>BaseStatComposite.export(MetricsCategory category)</code> , 该方法会收集内部三个聚合器的指标并返回。</strong></p> |
| <p>需要注意的是, 配置中心指标收集器不依赖于基本数据聚合器 导出数据,它在创建时将基本数据聚合器置为null,而使用自己的 updatedMetrics 字段存储采样:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ConfigCenterMetricsCollector</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>ConfigCenterMetric, AtomicLong<span style="color:#719e07">&gt;</span> updatedMetrics <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">ConfigCenterMetricsCollector</span>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//BaseStatComposite = null</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(<span style="color:#cb4b16">null</span>); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>混合指标收集器和数据聚合器之间呈现如下的包含关系:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/composite-struct.png" alt="composite-struct"></p> |
| <p><strong>DefaultMetricsCollector 默认指标采集器:</strong></p> |
| <p>它不直接存储采样数据,而是通过收集其下**指标采样器(MetricsSampler)**的样本来完成采样工作。这些采样器包括:</p> |
| <ul> |
| <li>方法采样器</li> |
| <li>应用采样器</li> |
| <li>线程池采样器</li> |
| </ul> |
| <p>这些采样器完成采样后,还会利用采集器中的**事件多播器(Multicaster)**将指标事件发布出去,可以被其它监听器处理。详细流程将在后文中探讨。</p> |
| <p><strong>HistogramMetricsCollector 直方图指标采集器:</strong></p> |
| <p>由于需要使用Timer完成直方图属性的统计,它使用自己的容器存储采样数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">HistogramMetricsCollector</span> <span style="color:#268bd2">implements</span> MetricsListener { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//方法指标样本与对应的Timer</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> ConcurrentHashMap<span style="color:#719e07">&lt;</span>MethodMetric, Timer<span style="color:#719e07">&gt;</span> rt <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap(); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>Timer(计时器)由 micrometer API 提供,常用于统计一分钟内的大量事件。</p></description></item><item><title>Blog: 2-指标收集器的指标采集流程</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/2-%E6%8C%87%E6%A0%87%E6%94%B6%E9%9B%86%E5%99%A8%E7%9A%84%E6%8C%87%E6%A0%87%E9%87%87%E9%9B%86%E6%B5%81%E7%A8%8B/</guid><description> |
| <h2 id="二指标收集器的指标采集流程">二、指标收集器的指标采集流程</h2> |
| <p>在前文中,我们了解了指标收集器(Collector)最终收集的数据只有三个来源:</p> |
| <ul> |
| <li> |
| <p>实现自混合指标收集器(CombMetricsCollector) 的元数据指标收集器(MetadataMetricsCollector)和注册中心指标收集器(RegistryMetricsCollector),它们的样本均存储在内置的基本数据聚合器中。具体来说,是基本数据聚合器下的四个子数据聚合器中:</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/composite-struct.png" alt="composite-struct"></p> |
| </li> |
| <li> |
| <p><strong>DefaultMetricsCollector 默认指标收集器</strong>,它的样本不仅来自于指标事件,还来自其下**采样器(Sampler)**中,用于Dubbo核心模块的采样。</p> |
| </li> |
| <li> |
| <p><strong>HistogramMetricsCollector 直方图指标收集器</strong>,由于采样数据的特殊性,它的样本直接以 Map 存储在内部。</p> |
| </li> |
| </ul> |
| <p>接下来,我们需要明确它们存储的指标是如何添加进去的。</p> |
| <h3 id="1服务治理模块的指标采集流程">1,服务治理模块的指标采集流程</h3> |
| <p>通过之前的分析,我们知道服务治理模块的指标采集器均实现自混合指标收集器(CombMetricsCollector)。它对基本数据聚合器(BaseStatComposite) 的大部分方法做了封装。基本数据聚合器又封装了四个负责存储不同类型指标采样的子聚合器。</p> |
| <p>这四个子聚合器包括:</p> |
| <ul> |
| <li>ApplicationStatComposite</li> |
| <li>ServiceStatComposite</li> |
| <li>MethodStatComposite</li> |
| <li>RtStatComposite</li> |
| </ul> |
| <p>实际上,<strong>元数据、注册中心指标收集器</strong>更新、添加指标的操作都是通过混合指标收集器暴露的方法进行。而具体的,是通过 <code>setNum</code>、<code>increment</code>、<code>addRt</code> 这三个方法(及它们的重载)进行操作。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//CombMetricsCollector</span> |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> BaseStatComposite stats; |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setNum</span>(MetricsKey metricsKey, String applicationName, String serviceKey, <span style="color:#dc322f">int</span> num) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.stats.setServiceKey(metricsKey, applicationName, serviceKey, num); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">increment</span>(String applicationName, MetricsKey metricsKey) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.stats.incrementApp(metricsKey, applicationName, SELF_INCREMENT_SIZE); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">increment</span>(String applicationName, String serviceKey, MetricsKey metricsKey, <span style="color:#dc322f">int</span> size) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.stats.incrementServiceKey(metricsKey, applicationName, serviceKey, size); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addRt</span>(String applicationName, String registryOpType, Long responseTime) { |
| </span></span><span style="display:flex;"><span> stats.calcApplicationRt(applicationName, registryOpType, responseTime); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addRt</span>(String applicationName, String serviceKey, String registryOpType, Long responseTime) { |
| </span></span><span style="display:flex;"><span> stats.calcServiceKeyRt(applicationName, serviceKey, registryOpType, responseTime); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>... |
| </span></span></code></pre></div><p>由于几个方法实际上的调用链路类似,我们选择从其中的 setNum 方法开始分析。</p> |
| <p>其在<strong>数据聚合器层面的调用链路</strong>可以总结为:setNum 方法调用基本数据聚合器的 setServiceKey 方法,该方法又会调用服务数据聚合器(ServiceStatComposite)的同名 setServiceKey 方法(我们已经知道基本数据聚合器内封装了四个不同类型的子聚合器),这个方法实质上是<strong>对应用层面的特定指标(由指标Key决定)进行注册并赋初始值(参数中的 num)。</strong></p> |
| <p>setNum 的用法均位于注册中心事件多播器(RegistryMetricsEventMulticaster)中声明的 MCat 接口中,在 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST 两个常量初始化时被调用。MCat 接口本身作为常量类使用,并在初始化时<strong>注册真正的指标常量</strong>:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//RegistryMetricsEventMulticaster.MCat</span> |
| </span></span><span style="display:flex;"><span>MetricsCat APPLICATION_NOTIFY_FINISH <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat(MetricsKey.NOTIFY_METRIC_NUM_LAST, |
| </span></span><span style="display:flex;"><span> (key, placeType, collector) <span style="color:#719e07">-&gt;</span> AbstractMetricsListener.onFinish(key, |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> collector.addRt(event.appName(), placeType.getType(), event.getTimePair().calc()); |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Integer<span style="color:#719e07">&gt;</span> lastNumMap <span style="color:#719e07">=</span> Collections.unmodifiableMap(event.getAttachmentValue(ATTACHMENT_KEY_LAST_NUM_MAP)); |
| </span></span><span style="display:flex;"><span> lastNumMap.forEach( |
| </span></span><span style="display:flex;"><span> (k, v) <span style="color:#719e07">-&gt;</span> collector.setNum(key, event.appName(), k, v)); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> )); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> MetricsCat APPLICATION_DIRECTORY_POST <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat(MetricsKey.DIRECTORY_METRIC_NUM_VALID, (key, placeType, collector) <span style="color:#719e07">-&gt;</span> AbstractMetricsListener.onEvent(key, |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey, Map<span style="color:#719e07">&lt;</span>String, Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> event.getAttachmentValue(ATTACHMENT_DIRECTORY_MAP); |
| </span></span><span style="display:flex;"><span> summaryMap.forEach((metricsKey, map) <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> map.forEach( |
| </span></span><span style="display:flex;"><span> (k, v) <span style="color:#719e07">-&gt;</span> collector.setNum(metricsKey, event.appName(), k, v))); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> )); |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//...</span> |
| </span></span></code></pre></div><p>此处声明的指标常量都是 MetricsCat 类型的。其中部分常量在创建时还传入了该指标的收集逻辑,如Key 为 NOTIFY_METRIC_NUM_LAST 的常量。以下为 MetricsCat 的定义:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsCat</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> MetricsPlaceType placeType; |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Function<span style="color:#719e07">&lt;</span>CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span>, AbstractMetricsListener<span style="color:#719e07">&gt;</span> eventFunc; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsCat</span>(MetricsKey metricsKey, BiFunction<span style="color:#719e07">&lt;</span>MetricsKey, CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span>, AbstractMetricsListener<span style="color:#719e07">&gt;</span> biFunc) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.eventFunc <span style="color:#719e07">=</span> collector <span style="color:#719e07">-&gt;</span> biFunc.apply(metricsKey, collector); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsCat</span>(MetricsKey metricsKey, TpFunction<span style="color:#719e07">&lt;</span>MetricsKey, MetricsPlaceType, CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span>, AbstractMetricsListener<span style="color:#719e07">&gt;</span> tpFunc) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.eventFunc <span style="color:#719e07">=</span> collector <span style="color:#719e07">-&gt;</span> tpFunc.apply(metricsKey, placeType, collector); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> MetricsCat <span style="color:#268bd2">setPlaceType</span>(MetricsPlaceType placeType) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.placeType <span style="color:#719e07">=</span> placeType; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">this</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Function<span style="color:#719e07">&lt;</span>CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span>, AbstractMetricsListener<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">getEventFunc</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> eventFunc; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//一个接受三个入参,一个返回值的函数接口。通过构造函数我们可以知道这三个入参分别是MetricsKey, MetricsPlaceType, CombMetricsCollector&lt;TimeCounterEvent&gt;,返回值为AbstractMetricsListener。</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@FunctionalInterface</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">TpFunction</span><span style="color:#719e07">&lt;</span>T, U, K, R<span style="color:#719e07">&gt;</span> { |
| </span></span><span style="display:flex;"><span> R <span style="color:#268bd2">apply</span>(T t, U u, K k); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>MetricsCat 类除了构造器,只提供了两个public方法,都是获取其内部属性的。</p> |
| <p>其实质上 eventFunc 字段的载体,提供了为特定指标生产监听器的逻辑,因此 <code>MetricsCat </code> 可以看做<strong>为特定指标生产指标监听器的工厂</strong>,用户在创建时传入这个监听器的处理逻辑。</p> |
| <p>通过泛型,我们可以知道它构造时使用的两个参数分别为 MetricsKey(指标Key)和一个接受 <code>MetricsKey, MetricsPlaceType, CombMetricsCollector&lt;TimeCounterEvent&gt;</code> 三个参数,返回一个 <code>AbstractMetricsListener</code> 的函数。之所以要多封装一层函数,是因为 <code>placeType</code> 字段在 <code>MetricsKey</code> 实例构造之后才会提供,借此实现延迟初始化。</p> |
| <p>回到之前两个<strong>在 MCat 中定义了监听器生产方法</strong>的两个常量的初始化流程:它们在创建 MetricsCat 时传入的TpFunction中定义的操作为:返回通过 AbstractMetricsListener.onFinish获取的事件完成监听器。当指定MetricsKey 的指标统计事件完成时,这个监听器中的 onEventFinish 方法就会被调用。 而 MetricsCat 构造时传入的 MetricsKey 会被作为 AbstractMetricsListener 的构造参数,用于指定监听的指标。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//RegistryMetricsEventMulticaster.MCat</span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">new</span> MetricsCat(MetricsKey.NOTIFY_METRIC_NUM_LAST, |
| </span></span><span style="display:flex;"><span> (key, placeType, collector) <span style="color:#719e07">-&gt;</span> AbstractMetricsListener.onFinish(key, |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> collector.addRt(event.appName(), placeType.getType(), event.getTimePair().calc()); |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Integer<span style="color:#719e07">&gt;</span> lastNumMap <span style="color:#719e07">=</span> Collections.unmodifiableMap(event.getAttachmentValue(ATTACHMENT_KEY_LAST_NUM_MAP)); |
| </span></span><span style="display:flex;"><span> lastNumMap.forEach( |
| </span></span><span style="display:flex;"><span> (k, v) <span style="color:#719e07">-&gt;</span> collector.setNum(key, event.appName(), k, v)); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> )); |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#586e75">//AbstractMetricsListener</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onFinish</span>(MetricsKey metricsKey, Consumer<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> finishFunc) { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#719e07">new</span> AbstractMetricsListener(metricsKey) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onEventFinish</span>(TimeCounterEvent event) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//此处是finishFunc就是之前 event -&gt;{...} 中定义的lambda函数</span> |
| </span></span><span style="display:flex;"><span> finishFunc.accept(event); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>三个形参 (key, placeType, collector) 中的 collector 为 <code>CombMetricsCollector&lt;TimeCounterEvent&gt;</code>,意味着它的三个实现(ConfigCenterMetricsCollector 、MetadataMetricsCollector、RegistryMetricsCollector)都可以作为参数。</p> |
| <p>至此,我们可以总结,对于这两个参数, <code>MetricsCat</code> 创建时嵌套的两层 lambda 函数最终是为了注册特定指标的监听器,并定义事件结束时的处理逻辑(内层的lambda)。在处理事件时,会调用混合指标收集器(CombMetricsCollector) 的 <code>addRT</code> 方法添加响应时间计时,还会调用 <code>setNum</code> 来添加指标计数。</p> |
| <p>由于此处的 <code>MetricsKey</code> 在 MetricsCat创建时就被传入,我们可以确定这两个字段存储了以下两个指标的统计逻辑:</p> |
| <ul> |
| <li> |
| <p>NOTIFY_METRIC_NUM_LAST:Last Notify Nums , 最后一个事件完成时的计数 。监听器中使用的是 <code>setNum</code>,事件结束时直接更新指定key指标的计数为传入的值,同时使用 <code>addRt </code> 来统计事件持续时长</p> |
| </li> |
| <li> |
| <p>DIRECTORY_METRIC_NUM_VALID:Valid Directory Urls,服务目录中注册成功的url数量。监听器中同样使用的是 <code>setNum</code>,事件结束后直接更新为服务目录中的最新计数</p> |
| </li> |
| </ul> |
| <p>之后,三个相关的 MetricsCat(指标类型) 实例会被绑定到一个 CategoryOverall(指标综合) 实例中,绑定的逻辑按一个事件进行的三个过程:<strong>事件发生、事件结束、事件失败</strong>,分别对应 CategoryOverall 的第2、3、4个参数,其中事件发生时的逻辑不能为 null。而第一个参数为 <code>MetricsPlaceType</code>,该参数封装了指标类型标识(如 register 服务注册、subscribe 服务订阅)和该指标的收集级别(应用还是服务)。</p> |
| <p>还记得 <code>MetricsCat</code> 中 <code>TpFunction</code> 的三个入参吗?其中第二个 placeType 就是这个参数。 <code>CategoryOverall</code> 在构造时会将它设置到其中的三个 MetricsCat 中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">// CategorySet:常量接口,同样位于RegistryMetricsEventMulticaster中</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">interface</span> <span style="color:#268bd2">CategorySet</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategoryOverall APPLICATION_NOTIFY <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall(OP_TYPE_NOTIFY, MCat.APPLICATION_NOTIFY_POST, MCat.APPLICATION_NOTIFY_FINISH, <span style="color:#cb4b16">null</span>); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategoryOverall SERVICE_DIRECTORY <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall(OP_TYPE_DIRECTORY, MCat.APPLICATION_DIRECTORY_POST, <span style="color:#cb4b16">null</span>, <span style="color:#cb4b16">null</span>); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategoryOverall SERVICE_REGISTER <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> CategoryOverall(OP_TYPE_REGISTER_SERVICE, MCat.SERVICE_REGISTER_POST, MCat.SERVICE_REGISTER_FINISH, MCat.SERVICE_REGISTER_ERROR); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//...</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>CategoryOverall<span style="color:#719e07">&gt;</span> ALL <span style="color:#719e07">=</span> Arrays.asList(APPLICATION_REGISTER, APPLICATION_SUBSCRIBE, APPLICATION_NOTIFY, SERVICE_DIRECTORY, SERVICE_REGISTER, SERVICE_SUBSCRIBE); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p><code>CategorySet</code> 中的常量都会被封装到List中,在 <code>RegistryMetricsEventMulticaster</code> 创建时统一调用:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">RegistryMetricsCollector</span> <span style="color:#268bd2">extends</span> CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">RegistryMetricsEventMulticaster</span>(RegistryMetricsCollector collector) { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> CategorySet.ALL.forEach(categorySet <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过 MetricsCat 实例中的定义的监听器创建逻辑,逐个注册监听器</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.addListener(categorySet.getPost().getEventFunc().apply(collector)); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (categorySet.getFinish() <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.addListener(categorySet.getFinish().getEventFunc().apply(collector)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (categorySet.getError() <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.addListener(categorySet.getError().getEventFunc().apply(collector)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//...</span> |
| </span></span></code></pre></div><p><strong>由此,我们也明确了 RegistryMetricsEventMulticaster (指标注册事件多播器)的作用</strong>:统一定义、管理事件,并在初始化时注册其中定义各种事件的<strong>监听器</strong>。</p> |
| <p>它继承了 SimpleMetricsEventMulticaster,其中的 publishEvent 方法在被调用时就会尝试调用所有监听器,判断其是否对当前事件类型感兴趣,选择是否进行调用。同时,这些监听器会对特定指标数据进行计算,更新到对应的收集器中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsEventMulticaster</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishEvent</span>(MetricsEvent event) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (event <span style="color:#719e07">instanceof</span> EmptyEvent) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (validateIfApplicationConfigExist(event)) <span style="color:#719e07">return</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (MetricsListener listener : listeners) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (listener.isSupport(event)) { |
| </span></span><span style="display:flex;"><span> listener.onEvent(event); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>我们通过分析混合指标收集器(CombMetricsCollector) 中的 setNum 方法的用法,了解到了 Composite 中的数据来源之一是注册指标事件多播器(RegistryMetricsEventMulticaster)中为服务注册相关指标创建的指标监听器。实际上,increment、addRt方法都是由指标监听器的各个实现调用的。</p> |
| <p>应用程序指标监听器(MetricsApplicationListener)中提供了 AbstractMetricsListener 的几个匿名实现,提供<strong>应用层面事件发生、完成、抛出异常三种情况下对给定指标的计数或RT的计算</strong>,大多数用做处理应用层面指标事件的 MetricsListener 都是它提供的三个监听器实现:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsApplicationListener</span> <span style="color:#268bd2">extends</span> AbstractMetricsListener { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsApplicationListener</span>(MetricsKey metricsKey) { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>(metricsKey); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//此处的Event均为TimeCounterEvent,在它被创建时就会自动开始计时</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onPostEventBuild</span>(MetricsKey metricsKey, CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener.onEvent(metricsKey, |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> collector.increment(event.appName(), metricsKey) |
| </span></span><span style="display:flex;"><span> ); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onFinishEventBuild</span>(MetricsKey metricsKey, MetricsPlaceType placeType, CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener.onFinish(metricsKey, |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> collector.increment(event.appName(), metricsKey); |
| </span></span><span style="display:flex;"><span> collector.addRt(event.appName(), placeType.getType(), event.getTimePair().calc()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">static</span> AbstractMetricsListener <span style="color:#268bd2">onErrorEventBuild</span>(MetricsKey metricsKey, MetricsPlaceType placeType, CombMetricsCollector<span style="color:#719e07">&lt;</span>TimeCounterEvent<span style="color:#719e07">&gt;</span> collector) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> AbstractMetricsListener.onError(metricsKey, |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> collector.increment(event.appName(), metricsKey); |
| </span></span><span style="display:flex;"><span> collector.addRt(event.appName(), placeType.getType(), event.getTimePair().calc()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>还有 MetricsServiceListener(服务指标监听器),它和 MetricsApplicationListener 十分类似,提供的是服务层面的指标监听器的通用实现,不再重复分析。</p> |
| <p><strong>可以用一句话简单的总结这三个 Collector 注册指标监听器的流程 : Collector 内部的 Mulicaster/Dispatcher 在被 Collector 创建时直接向自己注册已声明的指标监听器。</strong></p> |
| <p>至此,我们可以总结出 MetricsEvent 的部分消息转发路径 :</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/event-dispatch-simple.png" alt="event-dispatch-simple"></p> |
| <h3 id="2dubbo-核心模块的指标采集流程">2,Dubbo 核心模块的指标采集流程</h3> |
| <p>DefaultMetricsCollector(默认指标采集器) 作为指标采集器的默认实现,其主要通过采样器(Sampler)收集dubbo应用核心RPC功能的相关指标。 采样器包括以下几种:</p> |
| <ul> |
| <li>线程池线程状态(最大线程数、最小线程数、活跃线程数等),对应 <strong>ThreadPoolMetricsSampler,线程池指标采样器</strong></li> |
| <li>线程池中线程耗尽事件的计数,对应 <strong>ThreadRejectMetricsCountSampler, 线程耗尽次数采样器</strong></li> |
| <li>应用指标收集情况(收集次数),对应 DefaultMetricsCollector 中实现的 SimpleMetricsCountSampler 匿名子类</li> |
| </ul> |
| <p><strong>这些采样器内部会存储其负责采样类型指标的样本。由于默认指标采集器同样继承自 CombMetricsCollector,它也同时具有与前文中分析的三大中心指标收集器相似的指标转发流程。</strong></p> |
| <p>除了线程池指标采样器,其它两个采样器均实现自简单指标计数采样器(SimpleMetricsCountSampler)。它实现了通用的指标存取操作。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/sampler-struct.png" alt="sampler-struct"></p> |
| <p>简单指标计数采样器内部的指标样本容器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">abstract</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">SimpleMetricsCountSampler</span><span style="color:#719e07">&lt;</span>S, K, M <span style="color:#268bd2">extends</span> Metric<span style="color:#719e07">&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">implements</span> MetricsCountSampler<span style="color:#719e07">&lt;</span>S, K, M<span style="color:#719e07">&gt;</span> { |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> Map<span style="color:#719e07">&lt;</span>K, ConcurrentMap<span style="color:#719e07">&lt;</span>M, AtomicLong<span style="color:#719e07">&gt;&gt;</span> metricCounter <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p><strong>其中:泛型 M 为指标类型,如方法指标 MethodMetric;泛型 K 为指标名称类型,如 String;泛型 S 为请求源类型,如 String 或 Invocation。请求源用于定位触发采样的请求来源,指标名称则用于对指标进行分组,便于按名称来分组检索指标数据。</strong></p> |
| <p>以及对特定指标的增减操作:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsCountSampler</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">inc</span>(S source, K metricName) { |
| </span></span><span style="display:flex;"><span> doExecute(source, metricName, counter <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> counter.incrementAndGet(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span>; |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">dec</span>(S source, K metricName) { |
| </span></span><span style="display:flex;"><span> doExecute(source, metricName, counter <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> counter.decrementAndGet(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span>; |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">incOnEvent</span>(S source, K metricName) { |
| </span></span><span style="display:flex;"><span> doExecute(source, metricName, counter <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> counter.incrementAndGet(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">decOnEvent</span>(S source, K metricName) { |
| </span></span><span style="display:flex;"><span> doExecute(source, metricName, counter <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> counter.decrementAndGet(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>对于四个增加、减少计数的方法,它们最终都会调用 <code>doExecute</code> 方法来完成计数操作,其中 counter 函数定义了对计数器的操作(增加、减少)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsCountSampler</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExecute</span>(S source, K metricsName, Function<span style="color:#719e07">&lt;</span>AtomicLong, Boolean<span style="color:#719e07">&gt;</span> counter) { |
| </span></span><span style="display:flex;"><span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>S, K, M<span style="color:#719e07">&gt;</span> sampleConfigure <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> sampleConfigure.setSource(source); |
| </span></span><span style="display:flex;"><span> sampleConfigure.setMetricsName(metricsName); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//利用子类重写的countConfigure为 sampleConfigure 设置事件发布函数</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">this</span>.countConfigure(sampleConfigure); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//通过指标名获取对应的指标计数器</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>M, AtomicLong<span style="color:#719e07">&gt;</span> metricAtomic <span style="color:#719e07">=</span> metricCounter.get(metricsName); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metricAtomic <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> metricAtomic <span style="color:#719e07">=</span> metricCounter.computeIfAbsent(metricsName, k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Assert.notNull(sampleConfigure.getMetric(), <span style="color:#2aa198">&#34;metrics is null&#34;</span>); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> AtomicLong atomicCounter <span style="color:#719e07">=</span> metricAtomic.get(sampleConfigure.getMetric()); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (atomicCounter <span style="color:#719e07">==</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> atomicCounter <span style="color:#719e07">=</span> metricAtomic.computeIfAbsent(sampleConfigure.getMetric(), k <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> AtomicLong()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// counter函数定义了对atomicCounter的增减操作,如 inc方法定义的counter是对atomicCounter+1,dec方法定义的是对atomicCounter-1</span> |
| </span></span><span style="display:flex;"><span> Boolean isEvent <span style="color:#719e07">=</span> counter.apply(atomicCounter); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//如果本次计数操作应该触发事件...</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isEvent) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取子类设置的事件发布函数,发布事件</span> |
| </span></span><span style="display:flex;"><span> sampleConfigure.getFireEventHandler().accept(sampleConfigure); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p><code>doExecute</code> 做了两件事:</p> |
| <p>1,判断当前指标是否存在,如果不存在就放到容器中。</p> |
| <p>2,调用提供的计数函数对指标进行修改,对应 counter 字段。</p> |
| <p>以下为各采样器在 countConfigure 方法中提供的创建指标实例的逻辑:</p> |
| <ul> |
| <li>DefaultMetricsCollector 中 SimpleMetricsCountSampler 的匿名实现 (applicationSampler)提供的<code>countConfigure</code> 方法:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">countConfigure</span>( |
| </span></span><span style="display:flex;"><span> MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>String, MetricsEvent.Type, ApplicationMetric<span style="color:#719e07">&gt;</span> sampleConfigure) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供根据 configure 创建指标实例的函数</span> |
| </span></span><span style="display:flex;"><span> sampleConfigure.configureMetrics(configure <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ApplicationMetric(sampleConfigure.getSource())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><ul> |
| <li>ThreadRejectMetricsCountSampler 中提供的 <code>countConfigure</code> 方法:</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">countConfigure</span>(MetricsCountSampleConfigurer<span style="color:#719e07">&lt;</span>String, String, ThreadPoolRejectMetric<span style="color:#719e07">&gt;</span> sampleConfigure) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//提供根据 configure 创建指标实例的函数</span> |
| </span></span><span style="display:flex;"><span> sampleConfigure.configureMetrics(configure <span style="color:#719e07">-&gt;</span> <span style="color:#719e07">new</span> ThreadPoolRejectMetric(collector.getApplicationName(),configure.getSource())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><hr> |
| <p>默认指标收集器继承自 CombMetricsCollector,内部包含一个 DefaultSubDispatcher,因此它自身也可以作为指标事件的转发器,接受其它指标监听器的注册。</p> |
| <p>在之前,我们发现了 AggregateMetricsCollector(聚合指标收集器)会将自己注册为 DefaultMetricsCollector 的监听器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">AggregateMetricsCollector</span>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> registerListener(); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span>() { |
| </span></span><span style="display:flex;"><span>applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class).addListener(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>还有 HistogramMetricsCollector (直方图指标收集器)也会将自己注册为它的监听器。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">HistogramMetricsCollector</span>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> registerListener(); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span>() { applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class).getEventMulticaster().addListener(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p><strong>因此,聚合指标收集器和直方图指标收集器的指标事件来源于默认指标收集器转发的指标事件</strong>。通过默认指标转发器的 isSupport 方法,还可以发现这些指标事件的类型是 RequestEvent (RPC请求事件)或 RequestBeforeEvent(请求前失败事件:实际请求发送之前在 ClusterFilter 中产生的异常)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isSupport</span>(MetricsEvent event) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> event <span style="color:#719e07">instanceof</span> RequestEvent <span style="color:#719e07">||</span> event <span style="color:#719e07">instanceof</span> RequestBeforeEvent; |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/default-metrics-collector-struct.png" alt="default-metrics-collector-struct"></p> |
| <p><strong>至此,我们也明确了 Dubbo 应用内部核心模块的相关指标是如何收集的:默认指标收集器除了接受上层指标转发器的指标事件之外,还会通过各种采样器对埋点采样,通过 SubDispatcher 统一转发指标事件,通知注册为它的监听器的其它 Collector 完成采样。</strong></p> |
| <h3 id="3-直方图相关指标的采集流程">3, 直方图相关指标的采集流程</h3> |
| <p>直方图指标收集器(HistogramMetricsCollector)也是一个较为特殊的收集器,它主要负责RPC调用响应时间直方图指标这一种指标的收集。</p> |
| <p>由于直方图指标收集器只需要采集单一类型的指标,它直接使用Map来存储采样数据,而非更复杂的数据聚合器(Composite)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//HistogramMetricsCollector</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#268bd2">final</span> ConcurrentHashMap<span style="color:#719e07">&lt;</span>MethodMetric, Timer<span style="color:#719e07">&gt;</span> rt <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> ConcurrentHashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span></code></pre></div><p><strong>其中,key为方法指标,Timer则是该方法对应的RT计时器。该计时器由 micrometer 提供,在跟踪短时间内的大量事件时具有良好的性能。</strong></p> |
| <p>前文中已经提到,直方图指标收集器在初始化时会将自己注册为默认指标收集器(DefaultMetricsCollector)中的监听器,与聚合指标收集器相同(AggregateMetricsCollector)。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">registerListener</span>() { applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class).getEventMulticaster().addListener(<span style="color:#719e07">this</span>); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>这意味着它接收的指标事件实际也来自于默认指标收集器中的采样器。之前的分析中,我们知道默认指标收集器目前实际只转发来自 MetricsDispatcher 的请求相关事件,因此直方图指标收集器也只会收集请求响应时间相关的指标采样。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//HistogramMetricsCollector</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onRTEvent</span>(RequestEvent event) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metricRegister <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> MethodMetric metric <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MethodMetric(applicationModel.getApplicationName(), event.getAttachmentValue(MetricsConstants.INVOCATION)); |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">long</span> responseTime <span style="color:#719e07">=</span> event.getTimePair().calc(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> HistogramMetricSample sample <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HistogramMetricSample(MetricsKey.METRIC_RT_HISTOGRAM.getNameByType(metric.getSide()), |
| </span></span><span style="display:flex;"><span> MetricsKey.METRIC_RT_HISTOGRAM.getDescription(), metric.getTags(), RT); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Timer timer <span style="color:#719e07">=</span> ConcurrentHashMapUtils.computeIfAbsent(rt, metric, k <span style="color:#719e07">-&gt;</span> metricRegister.register(sample)); |
| </span></span><span style="display:flex;"><span> timer.record(responseTime, TimeUnit.MILLISECONDS); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>当接收到事件时,直方图指标收集器会先计算当前调用花费的时间,然后为计时器(Time)添加一条响应时间记录。</p></description></item><item><title>Blog: 3-指标监听注册梳理</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/3-%E6%8C%87%E6%A0%87%E7%9B%91%E5%90%AC%E6%B3%A8%E5%86%8C%E6%A2%B3%E7%90%86/</guid><description> |
| <h2 id="三指标监听注册梳理">三、指标监听注册梳理</h2> |
| <p>在前一章中,我们了解了不同收集器中的指标样本是如何被监听器添加进去的。接下来,我们将归纳指标监听器 的创建位置,及它们对应统计的指标。</p> |
| <p>通过之前的分析,我们已经知道指标 注册事件多播器(RegistryMetricsEventMulticaster)中定义了并绑定了服务注册相关的指标。这种绑定操作同样存在于其它几个简单指标事件多播器(SimpleMetricsEventMulticaster)的几个实现中。</p> |
| <h3 id="转发器注册">转发器注册</h3> |
| <p><strong>RegistrySubDispatcher (服务注册指标转发器)注册了服务注册相关指标:</strong></p> |
| <ul> |
| <li>应用级实例注册成功/失败/总数计数 (APPLICATION_REGISTER_&hellip;)</li> |
| <li>应用级服务接口订阅成功/失败/总数计数 (APPLICATION_SUBSCRIBE_&hellip;)</li> |
| <li>服务级注册成功/失败/总数计数 (SERVICE_REGISTER_&hellip;)</li> |
| <li>特殊的 APPLICATION_NOTIFY_FINISH 和 APPLICATION_DIRECTORY_POST (应用服务目录变化次数)</li> |
| </ul> |
| <p><strong>MetadataSubDispatcher(元数据指标转发器)注册应用元数据相关指标</strong></p> |
| <ul> |
| <li> |
| <p>应用推送元数据相关计数 (APPLICATION_PUSH_&hellip;)</p> |
| </li> |
| <li> |
| <p>应用订阅元数据相关计数 (APPLICAITON_SUBSCRIBE_&hellip;)</p> |
| </li> |
| <li> |
| <p>服务订阅元数据相关计数 (SERVICE_SUBSCRIBE_&hellip;)</p> |
| </li> |
| </ul> |
| <p><strong>ConfigCenterSubDispatcher (配置中心指标转发器) 注册配置中心配置更新次数指标</strong></p> |
| <ul> |
| <li>配置中心推送新配置次数 (CONFIGCENTER_METRIC_TOTAL)</li> |
| </ul> |
| <p><strong>DefaultSubDispatcher (默认转发器) 注册核心RPC调用次数指标</strong></p> |
| <ul> |
| <li>请求次数 (METRIC_REQUESTS)</li> |
| <li>请求成功次数(METRIC_REQUESTS_SUCCEED)</li> |
| <li>请求失败次数(METRIC_REQUEST_BUSINESS_FAILED)</li> |
| </ul> |
| <p><strong>MetricsDispatcher</strong></p> |
| <p>MetricsDispatcher 较为特殊,它负责 ApplicationModel 下所有 MetricsCollector(前文中提到的指标收集器) 的初始化注册工作,并将它们添加到自己的监听器列表中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">MetricsDispatcher</span> <span style="color:#268bd2">extends</span> SimpleMetricsEventMulticaster { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@SuppressWarnings</span>({<span style="color:#2aa198">&#34;rawtypes&#34;</span>}) |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsDispatcher</span>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> ScopeBeanFactory beanFactory <span style="color:#719e07">=</span> applicationModel.getBeanFactory(); |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> applicationModel.getExtensionLoader(MetricsCollector.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (extensionLoader <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> customizeCollectors <span style="color:#719e07">=</span> extensionLoader |
| </span></span><span style="display:flex;"><span> .getActivateExtensions(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (MetricsCollector customizeCollector : customizeCollectors) { |
| </span></span><span style="display:flex;"><span> beanFactory.registerBean(customizeCollector); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> customizeCollectors.forEach(<span style="color:#719e07">this</span>::addListener); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>需要注意,以上几个实现均继承自 SimpleMetricsEventMulticaster,因此它们都具有注册监听、转发事件的能力。它们将自己注册到对应领域的指标 Collector 中,并在收到指标事件时转发到自己注册的监听器中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//SimpleMetricsEventMulticaster</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">addListener</span>(MetricsListener<span style="color:#719e07">&lt;?&gt;</span> listener) { |
| </span></span><span style="display:flex;"><span> listeners.add(listener); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">publishEvent</span>(MetricsEvent event) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (event <span style="color:#719e07">instanceof</span> EmptyEvent) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (validateIfApplicationConfigExist(event)) <span style="color:#719e07">return</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (MetricsListener listener : listeners) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (listener.isSupport(event)) { |
| </span></span><span style="display:flex;"><span> listener.onEvent(event); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//...</span> |
| </span></span></code></pre></div><p><strong>SubDispatcher 和 Collector 之间的对应关系:</strong></p> |
| <ul> |
| <li>MetadataSubDispatcher -&gt; MetadataMetricsCollector 元数据指标事件</li> |
| <li>RegistrySubDispatcher -&gt; RegistryMetricsCollector 服务注册指标事件</li> |
| <li>ConfigCenterSubDispatcher -&gt; ConfigCenterMetricsCollector 配置中心指标事件</li> |
| <li>MetricsDispatcher 由 MetricsEventBus 通过 BeanFactory 加载。它是所有事件转发的入口。</li> |
| </ul> |
| <h3 id="事件触发">事件触发</h3> |
| <p>剩下的问题就是这些监听器是如何被触发的。</p> |
| <p>可以发现三大中心的指标转发器都是在它们对应的Collector中创建的:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">ConfigCenterMetricsCollector</span>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.setEventMulticaster(<span style="color:#719e07">new</span> ConfigCenterMetricsDispatcher(<span style="color:#719e07">this</span>)); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">MetadataMetricsCollector</span>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.setEventMulticaster(<span style="color:#719e07">new</span> MetadataMetricsEventMulticaster(<span style="color:#719e07">this</span>)); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">RegistryMetricsCollector</span>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">super</span>.setEventMulticaster(<span style="color:#719e07">new</span> RegistryMetricsEventMulticaster(<span style="color:#719e07">this</span>)); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>这意味这想要通过它们发布事件,需要通过它们对应的 <code>Collector</code> 来访问。</p> |
| <p>如前文所述, MetricsDispatcher 在初始化时会尝试获取并加载所有 MetricsCollector 的SPI拓展,</p> |
| <p>三大中心的MetricsCollector (Metadata/Registry/ConfigCenter)也会在这里被初始化,并添加为 MetricsDispatcher 的监听器:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#268bd2">MetricsDispatcher</span>(ApplicationModel applicationModel) { |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span> ExtensionLoader<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> extensionLoader <span style="color:#719e07">=</span> applicationModel.getExtensionLoader(MetricsCollector.class); |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (MetricsCollector customizeCollector : customizeCollectors) { |
| </span></span><span style="display:flex;"><span> beanFactory.registerBean(customizeCollector); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> customizeCollectors.forEach(<span style="color:#719e07">this</span>::addListener); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>对于 MetricsDispatcher,它由 MetricsEventBus 创建。而 MetricsEventBus 自身作为指标相关消息的总线,会接收所有指标消息,并将它们转发给监听者。</p> |
| <p>MetricsEvenetBus 提供了三个方法来发布指标事件:</p> |
| <ul> |
| <li><code>publish(MetricsEvent event)</code> ,将事件发布给所有订阅者,只发布一次且不关心事件处理结果</li> |
| <li><code>post(MetricsEvent event, Supplier&lt;T&gt; targetSupplier)</code> ,将事件发布给所有订阅者,并根据是否产生异常判断事件成功或失败,调用 MetricsDispatcher 发布对应的事件。</li> |
| <li><code>post(MetricsEvent event, Supplier&lt;T&gt; targetSupplier, Function&lt;T, Boolean&gt; trFunction)</code> ,额外的 trFunction 可用于通过业务结果判断事件成功或失败。 <code>targetSupplier</code> 为业务操作函数,泛型T为业务结果类型。</li> |
| </ul> |
| <p>这三个方法均会通过 MetricsDispatcher 来转发事件。</p> |
| <p>在之前的分析中,我们知道 MetricsDispatcher 创建了所有 MetricsCollector 拓展,并将它们注册为自己的监听者。</p> |
| <p>因此,当 MetricsEventBus 接收到发布的信息时,它会将信息转发到所有 MetricsCollector 中。对于 CombMetricsCollector 的实现,它们又会调用自己创建的 MetricsEventMulticaster 再次转发消息,到具体指标的监听器。</p> |
| <p>之后,这些监听器就会根据自己的逻辑修改Collector中的指标计数。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/metrics-source-blog/metris-event-dispatch-full.png" alt="image-20230629160012950"></p> |
| <h3 id="事件发布">事件发布</h3> |
| <p>接下来,我们将寻找指标事件发布的源头。</p> |
| <p>通过前文的分析,我们知道 MetricsEventBus 是所有指标事件发布的入口。具体来说,它有以下的用法:</p> |
| <ul> |
| <li>AbstractDirectory</li> |
| <li>ServiceConfig</li> |
| <li>DefaultApplicationDeployer</li> |
| <li>ApolloDynamicConfiguration</li> |
| <li>NacosDynamicConfiguration</li> |
| <li>ZookeeperDataListener</li> |
| <li>AbstractMetadataReport</li> |
| </ul> |
| <p>我们将逐个分析每个用法。</p> |
| <p><strong>AbstractDirectory</strong></p> |
| <p>AbstractDirectory 在修改 Invoker 状态相关的操作完成后都会通过 MetricsEventBus 发布 refreshDirectoryEvent(服务目录更新事件,类型为 RegistryEvent ),将当前目录中各种状态 Invoker 实例的最新数量作为附件添加到 RegistryEvent 中。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//AbstractDirectory</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">recoverDisabledInvoker</span>(Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;</span> invoker) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setInvokers</span>(BitList<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;&gt;</span> invokers) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">setInvokers</span>(BitList<span style="color:#719e07">&lt;</span>Invoker<span style="color:#719e07">&lt;</span>T<span style="color:#719e07">&gt;&gt;</span> invokers) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> Map<span style="color:#719e07">&lt;</span>MetricsKey, Map<span style="color:#719e07">&lt;</span>String, Integer<span style="color:#719e07">&gt;&gt;</span> <span style="color:#268bd2">getSummary</span>() { |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey, Map<span style="color:#719e07">&lt;</span>String, Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中可用的Invoker数量</span> |
| </span></span><span style="display:flex;"><span> summaryMap.put(MetricsKey.DIRECTORY_METRIC_NUM_VALID, groupByServiceKey(getValidInvokers())); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中不可用的Invoker数量</span> |
| </span></span><span style="display:flex;"><span> summaryMap.put(MetricsKey.DIRECTORY_METRIC_NUM_DISABLE, groupByServiceKey(getDisabledInvokers())); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//目录中等待重连的Invoker数量</span> |
| </span></span><span style="display:flex;"><span> summaryMap.put(MetricsKey.DIRECTORY_METRIC_NUM_TO_RECONNECT, groupByServiceKey(getInvokersToReconnect())); |
| </span></span><span style="display:flex;"><span> summaryMap.put(MetricsKey.DIRECTORY_METRIC_NUM_ALL, groupByServiceKey(getInvokers())); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> summaryMap; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>... |
| </span></span></code></pre></div><p>该事件最终会由 RegistryMetricsCollector 中的 RegistryMetricsDispatcher 转发到关系该事件的监听器中。<strong>事件和监听器之间通过 MetricsKey匹配</strong> 。</p> |
| <p>最终,MetricsKey 为 <code>**DIRECTORY_METRIC_NUM_VALID**</code> 的监听器会处理这个事件,并更新 Collector中的计数。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DIRECTORY_METRIC_NUM_VALID 对应的 Listener。</span> |
| </span></span><span style="display:flex;"><span>MetricsCat APPLICATION_DIRECTORY_POST <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> MetricsCat(MetricsKey.DIRECTORY_METRIC_NUM_VALID, (key, placeType, collector) <span style="color:#719e07">-&gt;</span> AbstractMetricsListener.onEvent(key, |
| </span></span><span style="display:flex;"><span> event <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>MetricsKey, Map<span style="color:#719e07">&lt;</span>String, Integer<span style="color:#719e07">&gt;&gt;</span> summaryMap <span style="color:#719e07">=</span> event.getAttachmentValue(ATTACHMENT_DIRECTORY_MAP); |
| </span></span><span style="display:flex;"><span> summaryMap.forEach((metricsKey, map) <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> map.forEach( |
| </span></span><span style="display:flex;"><span> (k, v) <span style="color:#719e07">-&gt;</span> collector.setNum(metricsKey, event.appName(), k, v))); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> )); |
| </span></span></code></pre></div><p>这样,服务目录中不同状态 Invoker 的计数就通过 RegistryMetricsCollector 更新到了 ServiceStatComposite 中。</p> |
| <p><strong>ServiceConfig</strong></p> |
| <p>当通过 ServiceConfig 导出、注册一个服务时,它会发布一个服务导出事件。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ServiceConfig </span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#268bd2">synchronized</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExport</span>() { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> doExportUrls(); |
| </span></span><span style="display:flex;"><span> exported(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doExportUrls</span>() { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> MetricsEventBus.post(RegistryEvent.toRsEvent(module.getApplicationModel(), getUniqueServiceName(), protocols.size() <span style="color:#719e07">*</span> registryURLs.size()), |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//该函数会被同步执行,如果抛出异常则触发 MetricsEvent 的 onError方法,否则触发 onFinish</span> |
| </span></span><span style="display:flex;"><span> () <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (ProtocolConfig protocolConfig : protocols) { |
| </span></span><span style="display:flex;"><span> String pathKey <span style="color:#719e07">=</span> URL.buildKey(getContextPath(protocolConfig) |
| </span></span><span style="display:flex;"><span> .map(p <span style="color:#719e07">-&gt;</span> p <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;/&#34;</span> <span style="color:#719e07">+</span> path) |
| </span></span><span style="display:flex;"><span> .orElse(path), group, version); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>serverService) { |
| </span></span><span style="display:flex;"><span> repository.registerService(pathKey, interfaceClass); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> doExportUrlsFor1Protocol(protocolConfig, registryURLs); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ); |
| </span></span><span style="display:flex;"><span> providerModel.setServiceUrls(urls); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>事件发布时,该事件会被转发到 RegistryMetricsCollector,触发对应的 Listener 增加 <strong>SERVICE_REGISTER_METRIC_REQUESTS</strong> (当前服务级注册请求总数)的计数,然后执行定义的 provider 函数。根据是否抛出异常,之后执行 onError 方法 或 onFinish 方法,增加 <strong>SERVICE_REGISTER_METRIC_REQUESTS_FAILED</strong> (当前服务级注册请求失败总数)或<strong>SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED</strong> (当前服务级注册请求成功总数) 的计数。</p> |
| <p><strong>DefaultApplicationDeployer</strong></p> |
| <p>它在应用部署过程中,初始化配置中心时,发布配置发生改变的事件。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">startConfigCenter</span>() { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> compositeDynamicConfiguration.addConfiguration( |
| </span></span><span style="display:flex;"><span> prepareEnvironment(configCenter) |
| </span></span><span style="display:flex;"><span> ); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">private</span> DynamicConfiguration <span style="color:#268bd2">prepareEnvironment</span>(ConfigCenterConfig configCenter) { |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// Add metrics</span> |
| </span></span><span style="display:flex;"><span> MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent(applicationModel, configCenter.getConfigFile(), configCenter.getGroup(),configCenter.getProtocol(), ConfigChangeType.ADDED.name(), configMap.size())); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (isNotEmpty(appGroup)) { |
| </span></span><span style="display:flex;"><span> MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent(applicationModel, appConfigFile, appGroup, configCenter.getProtocol(), ConfigChangeType.ADDED.name(), appConfigMap.size())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>在 <code>prepareEnvironment</code> 方法中,会按配置中心设置的组(group)和当前应用程序的名称作为组名发布两次事件。该事件会被转发到 ConfigCenterMetricsCollector,增加 <strong>CONFIGCENTER_METRIC_TOTAL</strong> (配置中心配置变化次数)的计数。</p> |
| <p><strong>ApolloDynamicConfiguration</strong></p> |
| <p>动态配置功能的 Apollo 配置中心实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ApolloDynamicConfiguration</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">onChange</span>(com.ctrip.framework.apollo.model.ConfigChangeEvent changeEvent) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent(applicationModel, event.getKey(), event.getGroup(),ConfigCenterEvent.APOLLO_PROTOCOL, ConfigChangeType.ADDED.name(), SELF_INCREMENT_SIZE)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>当Apollo配置中心的配置发生变化时,它的 <code>onChange</code> 方法会被触发,并在最后发布一个 ConfigCenterEvent。该事件最终转发到ConfigCenterMetricsCollector 中,同样增加 **CONFIGCENTER_METRIC_TOTAL **的计数。</p> |
| <p><strong>NacosDynamicConfiguration</strong></p> |
| <p>动态配置功能的 Nacos 配置中心实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//NacosDynamicConfiguration</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">innerReceive</span>(String dataId, String group, String configInfo) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent(applicationModel, event.getKey(), event.getGroup(), |
| </span></span><span style="display:flex;"><span> ConfigCenterEvent.NACOS_PROTOCOL, ConfigChangeType.ADDED.name(), SELF_INCREMENT_SIZE)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>当Nacos配置中心的配置发生变化时,它的 <code>innerReceive</code> 方法被触发,发布一个 ConfigCenterEvent。它的处理流程和 ApolloDynamicConfiguration 一致,最终增加 **CONFIGCENTER_METRIC_TOTAL **的计数。</p> |
| <p><strong>ZookeeperDataListener</strong></p> |
| <p>动态配置功能的 Zookeeper 实现。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//ZookeeperDataListener</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">dataChanged</span>(String path, Object value, EventType eventType) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> MetricsEventBus.publish(ConfigCenterEvent.toChangeEvent(applicationModel, configChangeEvent.getKey(), configChangeEvent.getGroup(), |
| </span></span><span style="display:flex;"><span> ConfigCenterEvent.ZK_PROTOCOL, ConfigChangeType.ADDED.name(), SELF_INCREMENT_SIZE)); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>在指标收集层面,它的行为和前文中两个配置中心一致,此处不详细展开三个配置中心具体实现的异同。</p> |
| <p><strong>AbstractMetadataReport</strong></p> |
| <p>元数据报告接口的抽象实现。它的三个实现 (NacosMetadataReport、RedisMetadataReport、ZookeeperMetadataReport)均使用它的指标事件逻辑。</p> |
| <p>当订阅新服务并获取它的元数据时,它会发布一个 MetadataEvent 触发相关指标的修改。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">storeProviderMetadataTask</span>(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span> MetricsEventBus.post(metadataEvent, () <span style="color:#719e07">-&gt;</span> |
| </span></span><span style="display:flex;"><span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> result <span style="color:#719e07">=</span> <span style="color:#cb4b16">true</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">try</span> { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> doStoreProviderMetadata(providerMetadataIdentifier, data); |
| </span></span><span style="display:flex;"><span> saveProperties(providerMetadataIdentifier, data, <span style="color:#cb4b16">true</span>, <span style="color:#719e07">!</span>syncReport); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> } <span style="color:#719e07">catch</span> (Exception e) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> result <span style="color:#719e07">=</span> <span style="color:#cb4b16">false</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> result; |
| </span></span><span style="display:flex;"><span> }, aBoolean <span style="color:#719e07">-&gt;</span> aBoolean |
| </span></span><span style="display:flex;"><span> ); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>在前文中,我们提到 MetricsEventBus.post 的第二个参数是实际要进行的业务操作,第三个参数则是根据业务操作返回值判断操作是否成功的逻辑。</p> |
| <p>此处的业务操作是尝试存储目标服务的元数据。执行操作之前,会先发布事件,最终增加 <strong>STORE_PROVIDER_METADATA</strong> (尝试存储服务元数据次数)的计数。如果产生异常,会增加 <strong>STORE_PROVIDER_METADATA_ERROR</strong> (存储服务元数据失败) 的计数,否则增加<strong>STORE_PROVIDER_METADATA_SUCCEED</strong>(存储服务元数据成功) 的计数。</p></description></item><item><title>Blog: 4-指标转换与导出</title><link>https://dubbo.apache.org/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</link><pubDate>Fri, 28 Apr 2023 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/blog/2023/04/28/4-%E6%8C%87%E6%A0%87%E8%BD%AC%E6%8D%A2%E4%B8%8E%E5%AF%BC%E5%87%BA/</guid><description> |
| <h2 id="四指标转换与导出">四、指标转换与导出</h2> |
| <p>本章主要梳理指标收集完成后,向外部收集器导出的流程。</p> |
| <p>通过之前的分析,我们知道不同类型指标的收集分别由各个 Collector 实现进行。它们底层的 MetricsCollector 接口定义了指标导出的操作。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">@SPI</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">MetricsCollector</span><span style="color:#719e07">&lt;</span>E <span style="color:#268bd2">extends</span> TimeCounterEvent<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">extends</span> MetricsLifeListener<span style="color:#719e07">&lt;</span>E<span style="color:#719e07">&gt;</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> <span style="color:#dc322f">boolean</span> <span style="color:#268bd2">isCollectEnabled</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">false</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * Collect metrics as {@link MetricSample} |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return List of MetricSample |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricSample<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">collect</span>(); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>而指标报告器 (MetricsReporter) 的实现会定时调用Collector 的 <code>collect</code> 方法,更新并导出指标数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">MetricsReporter</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//初始化</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">init</span>(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//刷新统计数据,定时调用collect()</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshData</span>(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取指标数据</span> |
| </span></span><span style="display:flex;"><span> String <span style="color:#268bd2">getResponse</span>(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取带指标名的指标样本(单个指标)</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">default</span> String <span style="color:#268bd2">getResponseWithName</span>(String metricsName) { <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span>; } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>指标报告器有两个实现:DefaultMetricsReporter 和 PrometheusMetricsReporter,它们都实现自 AbstractMetricsRepoter,并使用它的指标刷新逻辑 (<code>refreshData</code>方法)。</p> |
| <p>AbstractMetricsRepoter 初始化时会获取并保存所有 Collector 的实现,每次刷新数据,调用<code>refreshData</code>方法时都会遍历这些收集器,更新指标数据。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//AbstractMetricsRepoter</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">initCollectors</span>() { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricsCollector<span style="color:#719e07">&gt;</span> otherCollectors <span style="color:#719e07">=</span> beanFactory.getBeansOfType(MetricsCollector.class); |
| </span></span><span style="display:flex;"><span> collectors.addAll(otherCollectors); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">refreshData</span>() { |
| </span></span><span style="display:flex;"><span> collectors.forEach(collector <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> List<span style="color:#719e07">&lt;</span>MetricSample<span style="color:#719e07">&gt;</span> samples <span style="color:#719e07">=</span> collector.collect(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (MetricSample sample : samples) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将Dubbo的度量类型适配为micrometer的度量类型,并将其添加到CompositeMeterRegistry中,借此实现多监控系统的支持。</span> |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> }); |
| </span></span></code></pre></div><p>DefaultMetricsReporter 和 PrometheusMetricsReporter 各自实现了自己的指标采样逻辑 (<code>getResponse</code>方法)。</p> |
| <p><strong>PrometheusMetricsReporter</strong></p> |
| <p>它通过 PrometheusMeterRegistry 获取普罗米修斯支持格式的样本数据:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//PrometheusMetricsReporter</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponse</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> prometheusRegistry.scrape(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>在初始化时,它会开启一个定时任务,定时向普罗米修斯服务端推送采样数据:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//PrometheusMetricsReporter</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> SimpleMeterRegistry meterRegistry <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> SimpleMeterRegistry(); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doInit</span>() { |
| </span></span><span style="display:flex;"><span> addMeterRegistry(prometheusRegistry); |
| </span></span><span style="display:flex;"><span> schedulePushJob(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">schedulePushJob</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//这里的URL是DefaultMetricsReporter中定义的指标报告URL,提供了指标服务的具体地址</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">boolean</span> pushEnabled <span style="color:#719e07">=</span> url.getParameter(PROMETHEUS_PUSHGATEWAY_ENABLED_KEY, <span style="color:#cb4b16">false</span>); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (pushEnabled) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> pushJobExecutor.scheduleWithFixedDelay(() <span style="color:#719e07">-&gt;</span> push(pushGateway, job), pushInterval, pushInterval, TimeUnit.SECONDS); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">push</span>(PushGateway pushGateway, String job) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> refreshData(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//将本次采样数据添加到pushGateway,等待下次抓取</span> |
| </span></span><span style="display:flex;"><span> pushGateway.pushAdd(prometheusRegistry.getPrometheusRegistry(), job); |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><p>PushGateway 和 PrometheusRegistry 均为 micrometer 提供的 API。</p> |
| <p><strong>PrometheusRegistry 将度量数据转换为普罗米修斯支持的格式,而 PushGateway 存储样本,暴露一个HTTP端点供普罗米修斯服务端抓取。</strong></p> |
| <p>PushGateway 本身只是一个度量数据的缓存区,普罗米修斯服务端每从其中抓取一次数据,其内部的样本就会被清除。</p> |
| <p><strong>DefaultMetricsReporter</strong></p> |
| <p>指标报告器的默认实现,提供了按指标名称导出特定指标的方法。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DefaultMetricsReporter </span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponse</span>() { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> <span style="color:#cb4b16">null</span>; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> String <span style="color:#268bd2">getResponseWithName</span>(String metricsName) { |
| </span></span><span style="display:flex;"><span> ... |
| </span></span><span style="display:flex;"><span> meterRegistry.getMeters().stream().filter(meter <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//根据名称过滤样本</span> |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> metricsValue.forEach((key, value) <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//按格式拼装结果</span> |
| </span></span><span style="display:flex;"><span> }); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> sb.toString(); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doInit</span>() { |
| </span></span><span style="display:flex;"><span> addMeterRegistry(meterRegistry); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">protected</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">doDestroy</span>() {} |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>该实现使用的 SimpleMeterRegistry 本身只会存储指标数据的功能,而不像 PrometheusMeterRegistry 那样提供发布数据的方法。</p> |
| <p><strong>因此该指标报告器不会主动向外部发布数据,而是被动的通过 <code>getResponseWithName</code> 提供数据。</strong></p> |
| <p>而且,该报告器在任何情况下都会被初始化:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//DefaultApplicationDeployer#initMetricsReporter</span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">//If the protocol is not the default protocol, the default protocol is also initialized.</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (<span style="color:#719e07">!</span>PROTOCOL_DEFAULT.equals(metricsConfig.getProtocol())) { |
| </span></span><span style="display:flex;"><span> DefaultMetricsReporterFactory defaultMetricsReporterFactory <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> DefaultMetricsReporterFactory(applicationModel); |
| </span></span><span style="display:flex;"><span> MetricsReporter defaultMetricsReporter <span style="color:#719e07">=</span> defaultMetricsReporterFactory.createMetricsReporter(metricsConfig.toUrl()); |
| </span></span><span style="display:flex;"><span> defaultMetricsReporter.init(); |
| </span></span><span style="display:flex;"><span> applicationModel.getBeanFactory().registerBean(defaultMetricsReporter); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>... |
| </span></span></code></pre></div><p>实际上,该类主要在用户使用Qos命令查询时提供指定指标数据,而非提供给某个特定的外部指标中心。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#586e75">//Qos命令</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DefaultMetricsReporterCmd</span> <span style="color:#268bd2">implements</span> BaseCommand { |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">private</span> String <span style="color:#268bd2">getResponseByApplication</span>(ApplicationModel applicationModel, String metricsName) { |
| </span></span><span style="display:flex;"><span> String response <span style="color:#719e07">=</span> <span style="color:#2aa198">&#34;DefaultMetricsReporter not init&#34;</span>; |
| </span></span><span style="display:flex;"><span> MetricsReporter metricsReporter <span style="color:#719e07">=</span> applicationModel.getBeanFactory().getBean(DefaultMetricsReporter.class); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">if</span> (metricsReporter <span style="color:#719e07">!=</span> <span style="color:#cb4b16">null</span>) { |
| </span></span><span style="display:flex;"><span> metricsReporter.refreshData(); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">//获取指定名称指标的数据</span> |
| </span></span><span style="display:flex;"><span> response <span style="color:#719e07">=</span> metricsReporter.getResponseWithName(metricsName); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> response; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span>... |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>*需要注意的是,PrometheusMetricsReporter 也支持使用 Qos 命令查询内部指标数据,同样有对应的 PrometheusMetricsReporterCmd 实现,它们的工作原理相似。</p> |
| <p>以上就是指标样本从收集完成到最终导出到外部指标中心的大致流程。</p></description></item></channel></rss> |