从 Spring Boot 2.7.5 迁移到 Spring Boot 3.0.1 后。
指标 http.client.requests 上的标签 URI 在所有情况下都具有值 none。
我们构建restTemplate的URI,就像
URI uri = UriComponentsBuilder.fromUri(restTemplate.getUriTemplateHandler().expand("/hello"))
.build()
.toUri();
return restTemplate.getForObject(uri, String.class);
我习惯这样做,因为添加查询参数,我认为它更容易阅读和管理。
为了重现它,我在 这个存储库 中创建了两个小应用程序,一个在 Spring Boot 2.7.7 上,另一个在 3.0.1 上
我发现它与 Spring 6 上的新可观察性相关联,并与 Spring Boot 上的此问题相关联。
Spring boot 2 自定义 UriTemplateHandler 并将 urlTemplate 存储在 ThreadLocal
从文档我知道我可以通过提供新的@Bean ClientRequestObservationConvention来增强标签。
我还可以使用 org.springframework.web.client.RestTemplate#getForObject(java.lang.String, java.lang.Class, java.util.Map
return restTemplate.getForObject("/hello", String.class);
但我想知道是否有办法获得以前的行为并仍然使用org.springframework.web.client.RestTemplate#getForEntity(java.net.URI, java.lang.Class).
在 Spring Boot 3 和可观察性改进之前,
RestTemplate
确实会使用完整的请求 URI 作为“uri”标签。这是错误的,因为 URI 值在应用程序中不受限制,并且可能会导致指标系统中的基数爆炸。
例如,客户端可以使用
"/user/12"
、"/user/23"
等报告 uri 标签。正确的标签应该是低基数,并使用像 "/user/{userId}"
这样的请求模式。
在 Spring Boot 2.x 中,出于兼容性原因,我们没有改变此行为,而是添加了一个
MeterFilter
,一旦达到阈值,它就会停止记录指标。从 Spring Framework 6 开始,仪表直接内置于 RestTemplate
中。仅当 RestTemplate
方法接受字符串模板时才会记录此标记,就像您注意到的 restTemplate.getForObject("/hello", String.class)
一样。
您可以通过提供自己的
ClientRequestObservationConvention
来恢复到以前的行为(有关更多信息,请参阅 迁移 wiki ),但请注意,您还需要确保基数爆炸不会成为应用程序中的问题。
他是一些示例代码,用于让 URI 指标标记在 Spring Boot 3 中再次工作。定义 ExtendedServerRequestObservationConvention 类。
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
public class ExtendedServerRequestObservationConvention extends DefaultServerRequestObservationConvention {
@Override
public KeyValues getLowCardinalityKeyValues(final ServerRequestObservationContext context) {
return super.getLowCardinalityKeyValues(context).and(custom(context));
}
protected KeyValue custom(final ServerRequestObservationContext context) {
return KeyValue.of("uri", context.getCarrier().getRequestURI());
}
}
然后添加一个bean,以便Spring创建该类的实例
@Bean
public ExtendedServerRequestObservationConvention extendedServerRequestObservationConvention() {
return new ExtendedServerRequestObservationConvention();
}