SpringCloud升级之路2020.0.x版-15.UnderTow 订制

SpringCloud升级之路2020.0.x版-15.UnderTow 订制

本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

我们使用 Spring Boot 的 SPI 机制对 Undertow 进行订制,主要有如下两个方面:

需要在 accesslog 中打开响应时间统计。

期望通过 JFR 监控每个 Http 请求,同时占用空间不能太大。

接下来我们依次实现这两个需求:

image

首先,我们的框架作为基础组件,应该按照基础组件的标准来开发,使用 这个系列之前介绍的 spring.factories 这个 Spring Boot SPI 机制,在引入我们这个基础组件依赖的时候,就自动加载对应配置。

然后,对于是否打开响应时间统计,应该根据用户配置的 accesslog 格式而定(Undertow 的 accesslog 配置可以参考这个系列之前的文章)。

由此我们来编写代码。目前比较遗憾的是,Spring Boot 对接 Undertow 并没有直接的配置可以让 Undertow 打开响应时间统计,但是可以通过实现 WebServerFactoryCustomizer 接口的方式,对构造 WebServer 的 WebServerFactory 进行订制。其底层实现原理非常简单(以下参考源码:WebServerFactoryCustomizerBeanPostProcessor.java):

Spring Boot 中指定了 WebServerFactoryCustomizerBeanPostProcessor 这个 BeanPostProcessor.

WebServerFactoryCustomizerBeanPostProcessor 的 postProcessBeforeInitialization 方法(即在所有 Bean 初始化之前会调用的方法)中,如果 Bean 类型是 WebServerFactory,就将其作为参数传入注册的所有 WebServerFactoryCustomizer Bean 中进行自定义。

接下来我们来实现自定义的 WebServerFactoryCustomizer

DefaultWebServerFactoryCustomizer

package com.github.hashjang.spring.cloud.iiford.spring.cloud.webmvc.undertow; import io.undertow.UndertowOptions; import org.apache.commons.lang.StringUtils; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; public class DefaultWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory> { private final ServerProperties serverProperties; public DefaultWebServerFactoryCustomizer(ServerProperties serverProperties) { this.serverProperties = serverProperties; } @Override public void customize(ConfigurableUndertowWebServerFactory factory) { String pattern = serverProperties.getUndertow() .getAccesslog().getPattern(); // 如果 accesslog 配置中打印了响应时间,则打开记录请求开始时间配置 if (logRequestProcessingTiming(pattern)) { factory.addBuilderCustomizers(builder -> builder.setServerOption( UndertowOptions.RECORD_REQUEST_START_TIME, true ) ); } } private boolean logRequestProcessingTiming(String pattern) { //如果没有配置 accesslog,则直接返回 false if (StringUtils.isBlank(pattern)) { return false; } //目前只有 %D 和 %T 这两个占位符和响应时间有关,通过这个判断 //其他的占位符信息,请参考系列之前的文章 return pattern.contains("%D") || pattern.contains("%T"); } }

然后我们通过 spring.factories SPI 机制将这个类以一个单例 Bean 的形式,注册到我们应用 ApplicationContext 中,如图所示:

image

在 Configuration 和 spring.factories 之间多了一层 AutoConfiguration 的原因是:

隔离 SPI 与 Configuration,在 AutoConfiguration 同一管理相关的 Configuration。

@AutoConfigurationBefore 等类似的注解只能用在 SPI 直接加载的 AutoConfiguration 类上面才有效,隔离这一层也是出于这个考量。

image

在系列前面的文章中,我们提到过我们引入了 prometheus 的依赖。在引入这个依赖后,对于每个 http 请求,都会在请求结束返回响应的时候,将响应时间以及响应码和异常等,记入统计,其中的内容类似于:

http_server_requests_seconds_count{exception="None",method="POST",outcome="SUCCESS",status="200",uri="/query/orders",} 120796.0 http_server_requests_seconds_sum{exception="None",method="POST",outcome="SUCCESS",status="200",uri="/query/orders",} 33588.274025738 http_server_requests_seconds_max{exception="None",method="POST",outcome="SUCCESS",status="200",uri="/query/orders",} 0.1671125 http_server_requests_seconds_count{exception="MissingRequestHeaderException",method="POST",outcome="CLIENT_ERROR",status="400",uri="/query/orders",} 6.0 http_server_requests_seconds_sum{exception="MissingRequestHeaderException",method="POST",outcome="CLIENT_ERROR",status="400",uri="/query/orders",} 0.947300794 http_server_requests_seconds_max{exception="MissingRequestHeaderException",method="POST",outcome="CLIENT_ERROR",status="400",uri="/query/orders",} 0.003059704

可以看出,记录了从程序开始到现在,以 exception,method,outcome,status,uri 为 key 的调用次数,总时间和最长时间。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzjwjw.html