前语
在4. 分布式链路追寻客户端工具包Starter规划一文中,咱们完成了基础的Starter包,里边供给了咱们自己定义的Servlet过滤器和RestTemplate拦截器,其间Servlet过滤器叫做HoneyTracingFilter,仅供给了提取SpanContext,创立Span和开启Span的基础功用,所以本文将围绕怎么增强Servlet过滤器展开讨论。
相关版别依赖如下。
opentracing-api版别:0.33.0
opentracing-spring-web版别:4.1.0
jaeger-client版别:1.8.1
Springboot版别:2.7.6
正文
一. Opentracing供给的TracingFilter
其实最简略的增强方式,便是运用TracingFilter来替换咱们自己完成的HoneyTracingFilter,下面给出TracingFilter的源码完成。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
// 基于正则来判别当时恳求URI是否不需要记载链路信息
if (!isTraced(httpRequest, httpResponse)) {
chain.doFilter(httpRequest, httpResponse);
return;
}
if (servletRequest.getAttribute(SERVER_SPAN_CONTEXT) != null) {
chain.doFilter(servletRequest, servletResponse);
} else {
// 运用Extractor从HTTP恳求头中提取出SpanContext
SpanContext extractedContext = tracer.extract(Format.Builtin.HTTP_HEADERS,
new HttpServletRequestExtractAdapter(httpRequest));
// 创立Span并将其作为提取出来的Span的子Span
final Span span = tracer.buildSpan(httpRequest.getMethod())
.asChildOf(extractedContext)
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
.start();
httpRequest.setAttribute(SERVER_SPAN_CONTEXT, span.context());
// 在恳求的一开始运用装修器来装修Span
// 这儿的装修器是很重要的扩展点
for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {
spanDecorator.onRequest(httpRequest, span);
}
// 将创立出来的Span激活
try (Scope scope = tracer.activateSpan(span)) {
chain.doFilter(servletRequest, servletResponse);
if (!httpRequest.isAsyncStarted()) {
for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {
spanDecorator.onResponse(httpRequest, httpResponse, span);
}
}
} catch (Throwable ex) {
// 在恳求反常时运用装修器来装修Span
for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {
spanDecorator.onError(httpRequest, httpResponse, ex, span);
}
throw ex;
} finally {
if (httpRequest.isAsyncStarted()) {
// 异步Servlet场景下增加监听器
httpRequest.getAsyncContext()
.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent event) throws IOException {
HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();
HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();
// 异步操作完结时运用装修器装修Span
for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {
spanDecorator.onResponse(httpRequest,
httpResponse,
span);
}
span.finish();
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();
HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();
// 异步操作超时时运用装修器装修Span
for (ServletFilterSpanDecorator spanDecorator : spanDecorators) {
spanDecorator.onTimeout(httpRequest,
httpResponse,
event.getAsyncContext().getTimeout(),
span);
}
}
@Override
public void onError(AsyncEvent event) throws IOException {
HttpServletRequest httpRequest = (HttpServletRequest) event.getSuppliedRequest();
HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();
// 异步操作反常时运用装修器装修Span
for (ServletFilterSpanDecorator spanDecorator: spanDecorators) {
spanDecorator.onError(httpRequest,
httpResponse,
event.getThrowable(),
span);
}
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
}
});
} else {
// 恳求完结时设置完结时间并记载Span
span.finish();
}
}
}
}
经过阅读TracingFilter源码,咱们能够得到如下几种扩展增强。
- Servlet本身的urlPatterns机制。能够经过装备urlPatterns,决定哪些恳求需要打印链路信息;
- TracingFilter的skipPattern机制。能够经过装备skipPattern,决定哪些恳求不需要打印链路信息;
- 装修器ServletFilterSpanDecorator。能够供给ServletFilterSpanDecorator给到TracingFilter,这样在收到恳求,回来呼应和处理反常时均能够做一些扩展操作;
二. urlPatterns和skipPattern规划
在第一节中得到的TracingFilter的几种增强,其间第1和第2点的urlPatterns和skipPattern,能够供给出来供用户装备,本节对这部分进行完成。
首先是装备属性类里边需要参加urlPatterns和skipPattern的装备属性,如下所示。
/**
* 分布式链路追寻装备属性类。
*/
@ConfigurationProperties("honey.tracing")
public class HoneyTracingProperties {
private boolean enabled;
private HttpUrlProperties httpUrl = new HttpUrlProperties();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public HttpUrlProperties getHttpUrl() {
return httpUrl;
}
public void setHttpUrl(HttpUrlProperties httpUrl) {
this.httpUrl = httpUrl;
}
public static class HttpUrlProperties {
/**
* 按照/url1,/url2这样装备。
*/
private String urlPattern = "/*";
/**
* 按照/url1|/honey.*这样装备。
*/
private String skipPattern = "";
public String getUrlPattern() {
return urlPattern;
}
public void setUrlPattern(String urlPattern) {
this.urlPattern = urlPattern;
}
public String getSkipPattern() {
return skipPattern;
}
public void setSkipPattern(String skipPattern) {
this.skipPattern = skipPattern;
}
}
}
然后注册过滤器的装备类HoneyTracingFilterConfig需要做如下修正。
/**
* Servlet过滤器装备类。
*/
@Configuration
@AutoConfigureAfter(HoneyTracingConfig.class)
public class HoneyTracingFilterConfig {
@Autowired
private HoneyTracingProperties honeyTracingProperties;
@Bean
public FilterRegistrationBean<TracingFilter> honeyTracingFilter(Tracer tracer) {
String urlPattern = honeyTracingProperties.getHttpUrl().getUrlPattern();
String skipPatternStr = honeyTracingProperties.getHttpUrl().getSkipPattern();
Pattern skipPattern = Pattern.compile(skipPatternStr);
FilterRegistrationBean<TracingFilter> registrationBean = new FilterRegistrationBean<>();
// 为TracingFilter增加注册时的urlPattern
registrationBean.addUrlPatterns(urlPattern);
// TracingFilter的优先级要尽或许的高
registrationBean.setOrder(Integer.MIN_VALUE);
// 创立TracingFilter时指定装修器调集和skipPattern
// 这儿暂时传入空的装修器调集
registrationBean.setFilter(new TracingFilter(tracer, new ArrayList<>(), skipPattern));
return registrationBean;
}
}
三. TracingFilter的装修器规划
经过为TracingFilter注册ServletFilterSpanDecorator装修器,能够让咱们在收到恳求,回来呼应和处理反常时做一些扩展操作,例如记载恳求url,api和回来码等,下面完成一个装修器HoneyServletFilterSpanDecorator,其供给如下几个功用。
收到恳求时记载:
- 恳求的host;
- 恳求的api。
回来呼应时记载:
- 呼应码。
处理反常时记载:
- 呼应码。
完成如下。
/**
* {@link TracingFilter}的装修器。
*/
public class HoneyServletFilterSpanDecorator implements ServletFilterSpanDecorator {
@Override
public void onRequest(HttpServletRequest httpServletRequest, Span span) {
span.setTag(FIELD_HOST, getHostFromRequest(httpServletRequest));
span.setTag(FIELD_API, httpServletRequest.getRequestURI());
}
@Override
public void onResponse(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Span span) {
span.setTag(FIELD_HTTP_CODE, httpServletResponse.getStatus());
}
@Override
public void onError(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Throwable exception, Span span) {
span.setTag(FIELD_HTTP_CODE, httpServletResponse.getStatus());
}
@Override
public void onTimeout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, long timeout, Span span) {
// todo
}
private String getHostFromRequest(HttpServletRequest httpServletRequest) {
return httpServletRequest.getScheme()
+ "://"
+ httpServletRequest.getServerName()
+ ":"
+ httpServletRequest.getServerPort();
}
}
相关的常量字段记载在CommonConstants中,如下所示。
public class CommonConstants {
public static final double DEFAULT_SAMPLE_RATE = 1.0;
public static final String HONEY_TRACER_NAME = "HoneyTracer";
public static final String HONEY_REST_TEMPLATE_NAME = "HoneyRestTemplate";
public static final String FIELD_HOST = "host";
public static final String FIELD_API = "api";
public static final String FIELD_HTTP_CODE = "httpCode";
}
在注册TracingFilter时需要将HoneyServletFilterSpanDecorator设置给TracingFilter,对应的装备类HoneyTracingFilterConfig修正如下。
/**
* Servlet过滤器装备类。
*/
@Configuration
@AutoConfigureAfter(HoneyTracingConfig.class)
public class HoneyTracingFilterConfig {
@Autowired
private HoneyTracingProperties honeyTracingProperties;
@Bean
public FilterRegistrationBean<TracingFilter> honeyTracingFilter(Tracer tracer,
List<ServletFilterSpanDecorator> decorators) {
String urlPattern = honeyTracingProperties.getHttpUrl().getUrlPattern();
String skipPatternStr = honeyTracingProperties.getHttpUrl().getSkipPattern();
Pattern skipPattern = Pattern.compile(skipPatternStr);
FilterRegistrationBean<TracingFilter> registrationBean = new FilterRegistrationBean<>();
// 为TracingFilter增加注册时的urlPattern
registrationBean.addUrlPatterns(urlPattern);
// TracingFilter的优先级要尽或许的高
registrationBean.setOrder(Integer.MIN_VALUE);
// 创立TracingFilter时指定装修器调集和skipPattern
// 这儿暂时传入空的装修器调集
registrationBean.setFilter(new TracingFilter(tracer, decorators, skipPattern));
return registrationBean;
}
@Bean
public ServletFilterSpanDecorator honeyServletFilterSpanDecorator() {
return new HoneyServletFilterSpanDecorator();
}
}
至此,咱们就运用装修器装修了TracingFilter,作用便是终究在TracingFilter调用到Span的finish() 方法时,咱们能够从Span的tags中拿到本次恳求的host,api和httpCode,这些数据能够终究在打印链路日志时运用。
最终给出工程目录结构图。
总结
本文在4. 分布式链路追寻客户端工具包Starter规划的基础上,运用TracingFilter替换了咱们自己完成的HoneyTracingFilter,而且基于urlPatterns,skipPattern和装修器进行了扩展增强。