如何为Spring WebClient默认Micrometer指标添加自定义标签
Spring WebFlux WebClient 指标添加自定义标签最优实现方案
实现逻辑
利用Spring Actuator默认集成的Micrometer WebClient指标扩展点,将调用时传入的自定义标签存入请求属性,再通过自定义标签提供者读取属性注入到指标中,不需要修改原有指标的默认生成逻辑。
实现步骤
1. 定义自定义标签存储常量
定义统一的请求属性Key,用于存储调用时传入的自定义标签:
public class WebClientCustomTagConstants { // 请求属性中存储自定义标签的Key public static final String CUSTOM_TAGS_ATTR = "webClient.custom.tags"; }
2. 自定义标签提供者
继承默认的WebClient指标标签提供者,扩展读取自定义标签的逻辑:
import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; import org.springframework.boot.actuate.metrics.web.reactive.client.DefaultWebClientExchangeTagsProvider; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import java.util.Map; public class CustomWebClientExchangeTagsProvider extends DefaultWebClientExchangeTagsProvider { @Override public Iterable<Tag> tags(ClientRequest request, ClientResponse response, Throwable throwable) { // 保留原有默认标签 Tags tags = Tags.of(super.tags(request, response, throwable)); // 从请求属性中读取自定义标签 Map<String, String> customTags = request.attribute(WebClientCustomTagConstants.CUSTOM_TAGS_ATTR) .map(attr -> (Map<String, String>) attr) .orElse(Map.of()); // 追加自定义标签到指标 for (Map.Entry<String, String> entry : customTags.entrySet()) { tags = tags.and(Tag.of(entry.getKey(), entry.getValue())); } return tags; } }
3. 注册自定义标签提供者为Bean
Spring会自动使用该Bean替换默认的标签提供者,不需要修改原有WebClient的配置:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.boot.actuate.metrics.web.reactive.client.WebClientExchangeTagsProvider; @Configuration public class MetricsConfig { @Bean public WebClientExchangeTagsProvider customWebClientExchangeTagsProvider() { return new CustomWebClientExchangeTagsProvider(); } }
4. 封装WebClient调用方法支持传入自定义标签
注意不要每次调用都新建WebClient实例,预初始化复用即可,可根据需求选择Map传参或可变参数传参:
方式1:Map传参
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; import java.util.Map; @Component public class ShopApiClient { private final WebClient webClient; @Autowired public ShopApiClient(WebClient.Builder webClientBuilder) { // 预初始化WebClient,Spring会自动注入指标过滤器,无需手动配置 this.webClient = webClientBuilder .baseUrl("http://my-app.com") .build(); } public Mono<String> getShopsList(Map<String, String> extraTags) { return webClient.get() .uri("/shops") // 将自定义标签存入请求属性 .attribute(WebClientCustomTagConstants.CUSTOM_TAGS_ATTR, extraTags) .retrieve() .bodyToMono(String.class); } } // 调用示例 // Map<String, String> tags = Map.of("investor", "A", "version", "v1"); // shopApiClient.getShopsList(tags).subscribe();
方式2:可变参数传参(符合示例写法)
public Mono<String> getShopsList(String... extraTags) { if (extraTags.length % 2 != 0) { throw new IllegalArgumentException("标签必须为键值对成对出现"); } Map<String, String> tagMap = new HashMap<>(); for (int i = 0; i < extraTags.length; i += 2) { tagMap.put(extraTags[i], extraTags[i + 1]); } return webClient.get() .uri("/shops") .attribute(WebClientCustomTagConstants.CUSTOM_TAGS_ATTR, tagMap) .retrieve() .bodyToMono(String.class); } // 调用示例 // getShopsList("investor", "A", "version", "v1").subscribe();
注意事项
- 自定义标签的取值范围尽量小,避免出现大量唯一值导致Prometheus指标基数爆炸,影响监控服务性能
- 不要每次调用都调用
WebClient.Builder.build()新建实例,WebClient本身是线程安全的,复用即可
内容的提问来源于stack exchange,提问作者justAnotherDev
相关产品推荐
相关产品推荐

