好的,我们现在开始。
Dubbo 3.3 可观测性指标 Micrometer Tag 自定义:DubboMetrics 与 MetricFilter
大家好,今天我们来深入探讨 Dubbo 3.3 中可观测性指标 Micrometer Tag 自定义,重点讲解 DubboMetrics 和 MetricFilter 的使用。可观测性对于分布式系统的稳定运行至关重要,而 Dubbo 作为流行的 RPC 框架,提供了丰富的可观测性支持。通过自定义 Micrometer Tag,我们可以更精细地监控 Dubbo 服务的运行状态,从而更好地诊断和解决问题。
1. DubboMetrics 概述
DubboMetrics 是 Dubbo 集成 Micrometer 的核心组件之一。它负责收集 Dubbo 运行时的各种指标,并将这些指标注册到 Micrometer 的 MeterRegistry 中。这些指标可以被 Prometheus、InfluxDB 等监控系统采集,从而实现对 Dubbo 服务的监控。
DubboMetrics 默认会收集一些基本的指标,例如:
- dubbo.consumer.invocations.total: Consumer 端总的调用次数。
- dubbo.consumer.invocations.active: Consumer 端当前活跃的调用次数。
- dubbo.consumer.invocations.failed: Consumer 端调用失败的次数。
- dubbo.provider.invocations.total: Provider 端总的调用次数。
- dubbo.provider.invocations.active: Provider 端当前活跃的调用次数。
- dubbo.provider.invocations.failed: Provider 端调用失败的次数。
- dubbo.provider.process.time: Provider 端处理请求的时间。
除了这些基本指标,DubboMetrics 还允许我们自定义 Tag,以便更细粒度地分析指标数据。例如,我们可以根据服务接口、方法名、应用名等信息来添加 Tag。
2. MetricFilter 概述
MetricFilter 是 Dubbo 提供的一个 SPI 接口,用于在 DubboMetrics 收集指标时添加自定义的 Tag。通过实现 MetricFilter 接口,我们可以拦截 Dubbo 的调用请求,并根据请求的上下文信息来添加 Tag。
MetricFilter 接口定义如下:
package org.apache.dubbo.metrics.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import java.util.List;
@SPI
public interface MetricFilter {
/**
* Add metrics tags.
*
* @param invoker service invoker
* @param invocation service invocation
* @param url service url
* @param metricList Metric list
* @param isProvider is provider
*/
void addTags(Invoker<?> invoker, Invocation invocation, URL url, List<String> metricList, boolean isProvider);
/**
* Before invoke.
*
* @param invoker service invoker
* @param invocation service invocation
* @param url service url
* @param isProvider is provider
*/
void beforeInvoke(Invoker<?> invoker, Invocation invocation, URL url, boolean isProvider);
/**
* After invoke.
*
* @param invoker service invoker
* @param invocation service invocation
* @param url service url
* @param result service result
* @param isProvider is provider
*/
void afterInvoke(Invoker<?> invoker, Invocation invocation, URL url, Result result, boolean isProvider);
/**
* On invoke exception.
*
* @param invoker service invoker
* @param invocation service invocation
* @param url service url
* @param exception service exception
* @param isProvider is provider
*/
void onInvokeException(Invoker<?> invoker, Invocation invocation, URL url, Throwable exception, boolean isProvider);
}
该接口定义了五个方法:
addTags(): 用于添加自定义 Tag。该方法接收Invoker、Invocation、URL、metricList和isProvider作为参数。Invoker代表服务调用者,Invocation代表调用信息,URL代表服务的 URL,metricList是一个字符串列表,用于存储要添加的 Tag,isProvider表示当前是否为 Provider 端。beforeInvoke(): 在服务调用之前执行。afterInvoke(): 在服务调用之后执行。onInvokeException(): 在服务调用发生异常时执行。
我们可以通过实现 MetricFilter 接口,并实现 addTags 方法来添加自定义 Tag。同时,也可以利用 beforeInvoke、afterInvoke 和 onInvokeException 方法来收集一些额外的指标信息。
3. 自定义 MetricFilter 实现
下面我们通过一个例子来说明如何自定义 MetricFilter 来添加自定义 Tag。假设我们需要根据请求的参数来添加 Tag。
首先,我们需要创建一个类来实现 MetricFilter 接口:
package com.example.dubbo.metrics;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.metrics.filter.MetricFilter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import java.util.List;
@Activate(group = {"consumer", "provider"})
public class CustomMetricFilter implements MetricFilter {
@Override
public void addTags(Invoker<?> invoker, Invocation invocation, URL url, List<String> metricList, boolean isProvider) {
String parameter = invocation.getArgument(0, String.class); // 假设第一个参数是 String 类型
if (parameter != null) {
metricList.add("parameter=" + parameter);
}
}
@Override
public void beforeInvoke(Invoker<?> invoker, Invocation invocation, URL url, boolean isProvider) {
// 可以添加一些前置处理逻辑
}
@Override
public void afterInvoke(Invoker<?> invoker, Invocation invocation, URL url, Result result, boolean isProvider) {
// 可以添加一些后置处理逻辑
}
@Override
public void onInvokeException(Invoker<?> invoker, Invocation invocation, URL url, Throwable exception, boolean isProvider) {
// 可以添加一些异常处理逻辑
}
}
在这个例子中,我们实现了 addTags 方法,从 Invocation 中获取第一个参数,并将其作为 Tag 添加到 metricList 中。 @Activate(group = {"consumer", "provider"}) 注解表示该 MetricFilter 将在 Consumer 端和 Provider 端都生效。
接下来,我们需要将这个 MetricFilter 注册到 Dubbo 中。 Dubbo 使用 SPI (Service Provider Interface) 机制来加载 MetricFilter。 因此,我们需要在 src/main/resources/META-INF/dubbo/org.apache.dubbo.metrics.filter.MetricFilter 文件中添加以下内容:
custom=com.example.dubbo.metrics.CustomMetricFilter
这样,Dubbo 启动时就会加载 CustomMetricFilter,并在收集指标时调用 addTags 方法。
4. 配置 Micrometer
为了使 Dubbo 的指标能够被监控系统采集,我们需要配置 Micrometer。 Dubbo 3.3 默认使用 Micrometer 作为指标收集工具,因此我们需要添加 Micrometer 的依赖。
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.10.0</version> <!-- 使用最新版本 -->
</dependency>
<!-- Prometheus MeterRegistry -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.10.0</version> <!-- 使用最新版本 -->
</dependency>
然后,我们需要配置 Micrometer 的 MeterRegistry。 可以通过 Spring Boot 的配置来完成:
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MetricsConfiguration {
@Bean
public MeterRegistry meterRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
}
这段代码创建了一个 PrometheusMeterRegistry,并将它注册为 Spring Bean。 这样,Dubbo 就会将指标注册到 PrometheusMeterRegistry 中,然后 Prometheus 就可以从 PrometheusMeterRegistry 中采集指标数据。
5. 验证自定义 Tag
配置完成后,我们可以启动 Dubbo 服务,并调用服务接口。 然后,我们可以通过 Prometheus 的 Web 界面来查看指标数据。
假设我们的服务接口如下:
public interface DemoService {
String sayHello(String name);
}
并且我们在 Consumer 端调用 sayHello 方法,传入的参数是 "World"。 那么,我们应该能够在 Prometheus 中看到包含 parameter=World Tag 的指标数据。
例如,dubbo_consumer_invocations_total{interface="com.example.DemoService", method="sayHello", parameter="World", ...}。
6. 更复杂的 MetricFilter 实现
除了根据请求参数添加 Tag,我们还可以根据其他信息来添加 Tag。 例如,我们可以根据用户的身份信息来添加 Tag,从而实现对不同用户的请求进行监控。
package com.example.dubbo.metrics;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.metrics.filter.MetricFilter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import java.util.List;
import java.util.Map;
@Activate(group = {"consumer", "provider"})
public class UserMetricFilter implements MetricFilter {
@Override
public void addTags(Invoker<?> invoker, Invocation invocation, URL url, List<String> metricList, boolean isProvider) {
Map<String, Object> attachments = RpcContext.getContext().getObjectAttachments();
String userId = (String) attachments.get("userId");
if (userId != null) {
metricList.add("userId=" + userId);
}
}
@Override
public void beforeInvoke(Invoker<?> invoker, Invocation invocation, URL url, boolean isProvider) {
// 可以添加一些前置处理逻辑
}
@Override
public void afterInvoke(Invoker<?> invoker, Invocation invocation, URL url, Result result, boolean isProvider) {
// 可以添加一些后置处理逻辑
}
@Override
public void onInvokeException(Invoker<?> invoker, Invocation invocation, URL url, Throwable exception, boolean isProvider) {
// 可以添加一些异常处理逻辑
}
}
在这个例子中,我们从 RpcContext 中获取 userId,并将其作为 Tag 添加到 metricList 中。 使用 RpcContext 需要注意在调用服务前设置 attachment。 例如,在 Consumer 端:
RpcContext.getContext().setAttachment("userId", "123");
String result = demoService.sayHello("World");
7. 总结
通过自定义 MetricFilter,我们可以灵活地添加自定义 Tag,从而更细粒度地监控 Dubbo 服务的运行状态。 这对于诊断和解决问题非常有帮助。 我们可以根据实际需求,添加不同的 Tag,例如根据请求参数、用户身份、业务类型等信息来添加 Tag。 结合 Micrometer 和 Prometheus 等监控系统,我们可以构建完善的可观测性体系,从而更好地保障 Dubbo 服务的稳定运行。
8. 扩展思路
以下是一些扩展思路,可用于更深入地利用 DubboMetrics 和 MetricFilter 进行可观测性建设:
- 动态 Tag 配置: 将 Tag 配置从代码中解耦出来,通过配置文件或配置中心动态调整 Tag 的添加规则。
- 采样策略: 对于高并发场景,可以实现采样策略,只对部分请求添加 Tag,以减少性能开销。
- 自定义指标: 除了添加 Tag,还可以自定义指标,例如统计特定业务逻辑的执行时间。
- 结合链路追踪: 将 Micrometer 指标与链路追踪系统(例如 SkyWalking)结合,可以更全面地了解请求的调用链和性能瓶颈。
9. 常见问题与注意事项
- 性能影响: 添加过多的 Tag 可能会影响性能,需要根据实际情况进行权衡。
- Tag 命名规范: 建议使用统一的 Tag 命名规范,方便查询和分析指标数据。
- 避免敏感信息: 避免将敏感信息添加到 Tag 中,例如密码、身份证号等。
- MetricFilter 的顺序: 如果定义了多个
MetricFilter,它们的执行顺序是不确定的,需要注意它们之间的依赖关系。
10. 代码示例
以下是一个完整的代码示例,演示了如何自定义 MetricFilter 并配置 Micrometer:
1. 服务接口 (DemoService.java):
package com.example.dubbo;
public interface DemoService {
String sayHello(String name);
}
2. 服务实现 (DemoServiceImpl.java):
package com.example.dubbo.impl;
import com.example.dubbo.DemoService;
import org.apache.dubbo.config.annotation.DubboService;
@DubboService
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello, " + name;
}
}
3. 自定义 MetricFilter (CustomMetricFilter.java):
package com.example.dubbo.metrics;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.metrics.filter.MetricFilter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import java.util.List;
import java.util.Map;
@Activate(group = {"consumer", "provider"})
public class CustomMetricFilter implements MetricFilter {
@Override
public void addTags(Invoker<?> invoker, Invocation invocation, URL url, List<String> metricList, boolean isProvider) {
Map<String, Object> attachments = RpcContext.getContext().getObjectAttachments();
String userId = (String) attachments.get("userId");
if (userId != null) {
metricList.add("userId=" + userId);
}
}
@Override
public void beforeInvoke(Invoker<?> invoker, Invocation invocation, URL url, boolean isProvider) {
// 可以添加一些前置处理逻辑
}
@Override
public void afterInvoke(Invoker<?> invoker, Invocation invocation, URL url, Result result, boolean isProvider) {
// 可以添加一些后置处理逻辑
}
@Override
public void onInvokeException(Invoker<?> invoker, Invocation invocation, URL url, Throwable exception, boolean isProvider) {
// 可以添加一些异常处理逻辑
}
}
4. MetricFilter 配置文件 (src/main/resources/META-INF/dubbo/org.apache.dubbo.metrics.filter.MetricFilter):
custom=com.example.dubbo.metrics.CustomMetricFilter
5. Micrometer 配置 (MetricsConfiguration.java):
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MetricsConfiguration {
@Bean
public MeterRegistry meterRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
}
6. Spring Boot 启动类 (DubboApplication.java):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DubboApplication {
public static void main(String[] args) {
SpringApplication.run(DubboApplication.class, args);
}
}
7. Consumer 端调用示例:
import com.example.dubbo.DemoService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcContext;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class ConsumerRunner implements CommandLineRunner {
@DubboReference
private DemoService demoService;
@Override
public void run(String... args) throws Exception {
RpcContext.getContext().setAttachment("userId", "123");
String result = demoService.sayHello("World");
System.out.println(result);
}
}
8. Maven 依赖 (pom.xml):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>dubbo-metrics</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dubbo-metrics</name>
<description>dubbo-metrics</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
<!-- Zookeeper dependencies -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.10.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
11. 总结:自定义Tag,提升可观测性
本文详细介绍了 Dubbo 3.3 中 DubboMetrics 和 MetricFilter 的使用,以及如何自定义 Tag 来更精细地监控 Dubbo 服务的运行状态。 通过代码示例,展示了如何实现自定义 MetricFilter,并配置 Micrometer 和 Prometheus。 通过自定义 Tag,可以实现更精细的可观测性,帮助我们更好地诊断和解决 Dubbo 服务的问题。