Spring Boot Webflux中NettyWebServerFactoryCustomizer未触发及Metrics配置问题
关于NettyWebServerFactoryCustomizer的两个问题解答
问题1:customize方法未触发的原因及解决
可能的原因和对应解决方式:
- 配置类未被Spring扫描:确保
@Configuration注解的类所在包在Spring Boot的组件扫描范围内(默认是启动类所在包及其子包),如果不在,需要手动通过@ComponentScan指定扫描路径。 - 项目不是Reactive栈:如果你的项目依赖的是
spring-boot-starter-web(Servlet栈),Netty不会作为Web服务器被使用,NettyReactiveWebServerFactory也不会被实例化,定制器自然不会触发。需要切换为Reactive栈,将依赖改为spring-boot-starter-webflux。 - 依赖缺失:确保项目中引入了
spring-boot-starter-webflux依赖,否则Netty相关的工厂类不会被加载。
另外,关于开启Netty metrics,Spring Boot有自动配置支持:只要引入spring-boot-starter-actuator和对应的Micrometer注册中心依赖(如micrometer-registry-prometheus),并在配置文件中开启相关配置(比如management.endpoints.web.exposure.include=metrics),Spring Boot会自动为Netty配置metrics,无需手动编写定制器。
问题2:metrics方法的uriTagValue参数示例及避免基数爆炸
问题说明
直接使用s->s会将原始请求URI(如/api/user/123)作为标签值,不同的路径参数会生成大量不同的标签,导致基数爆炸,影响监控系统性能。
获取模板化URL的实现示例
要获取类似/api/user/{id}的模板化URL,需要结合Spring Web的请求上下文和Netty的Channel属性传递信息:
- 编写WebFilter传递模板化路径
创建WebFilter,在请求处理时将Spring匹配到的模板化路径存入Netty Channel属性:
@Component public class TemplateUriWebFilter implements WebFilter { private static final AttributeKey<String> TEMPLATE_URI_ATTR = AttributeKey.valueOf("TEMPLATE_URI"); @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { // 获取Spring匹配到的模板化路径 String templateUri = exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); if (templateUri != null) { // 将模板路径存入Netty Channel的属性中 Channel channel = exchange.getRequest().getNativeRequest(Channel.class); channel.attr(TEMPLATE_URI_ATTR).set(templateUri); } return chain.filter(exchange); } }
- 在定制器中读取模板化路径
修改NettyWebServerFactoryCustomizer的metrics配置,从Channel属性读取模板化路径,不存在则 fallback 到原始URI:
@Configuration public class NettyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> { private static final AttributeKey<String> TEMPLATE_URI_ATTR = AttributeKey.valueOf("TEMPLATE_URI"); @Override public void customize(NettyReactiveWebServerFactory factory) { factory.addServerCustomizers(httpServer -> { return httpServer .wiretap(true) .metrics(true, request -> { Channel channel = request.channel(); String templateUri = channel.attr(TEMPLATE_URI_ATTR).get(); // 优先使用模板化路径,否则用原始URI return templateUri != null ? templateUri : request.uri(); }) .doOnConnection(conn -> { conn.addHandlerFirst(new ReadTimeoutHandler(50, TimeUnit.MILLISECONDS)); }); }); } }
这样就能确保metrics中的uri标签使用模板化路径,避免基数爆炸问题。
内容的提问来源于stack exchange,提问作者gfan
相关产品推荐
相关产品推荐

