前言
上一节我们了解到,如果URI上面有动态参数,那么默认的sleuth拿到的不是真实的接口路径,这会导致后续我们做接口调用链的统计的时候会有问题,本文基于 sleuth1.2.6版本, 低于这个版本,这种方案无效。
新建Filter
新建traceLocalFilter , 该类继承TraceFilter。 这个类里面的源码从TraceFilter里面拷贝,比较原装的代码还是比较好的,我们只是需要在上面该一些东西而已。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
if (!(servletRequest instanceof HttpServletRequest) || !(servletResponse instanceof HttpServletResponse)) {
throw new ServletException("Filter just supports HTTP requests");
}
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// ------------- 源码添加 --------------
MatchableHandlerMapping matchableHandlerMapping = null;
try {
// 获取最佳匹配的HandlerMapping
matchableHandlerMapping = this.mappingIntrospector.getMatchableHandlerMapping(request);
if (matchableHandlerMapping != null) {
// 获取本次请求的Handler, 重点就在这里,调用这个方法之后,会自动在request的attribute中添加
// 本次请求的最佳匹配路径,这里是真实路径
matchableHandlerMapping.getHandler(request);
}
} catch (Exception e) {
e.printStackTrace();
}
// 从request中获取真实路径
String uri = getMatchingPattern(request);
// -------------- 源码添加结束 --------------
boolean skip = this.skipPattern.matcher(uri).matches()
|| Span.SPAN_NOT_SAMPLED.equals(ServletHeadUtils.getHeader(request, response, Span.SAMPLED_NAME));
Span spanFromRequest = getSpanFromAttribute(request);
if (spanFromRequest != null) {
continueSpan(request, spanFromRequest);
}
if (log.isDebugEnabled()) {
log.debug("Received a request to uri [" + uri + "] that should not be sampled [" + skip + "]");
}
// in case of a response with exception status a exception controller will close the span
if (!httpStatusSuccessful(response) && isSpanContinued(request)) {
Span parentSpan = parentSpan(spanFromRequest);
processErrorRequest(filterChain, request, new TraceHttpServletResponse(response, parentSpan), spanFromRequest);
return;
}
String name = HTTP_COMPONENT + ":" + uri;
Throwable exception = null;
try {
spanFromRequest = createSpan(request, skip, spanFromRequest, name);
filterChain.doFilter(request, new TraceHttpServletResponse(response, spanFromRequest));
} catch (Throwable e) {
exception = e;
errorParser().parseErrorTags(tracer().getCurrentSpan(), e);
throw e;
} finally {
if (isAsyncStarted(request) || request.isAsyncStarted()) {
if (log.isDebugEnabled()) {
log.debug("The span " + spanFromRequest + " will get detached by a HandleInterceptor");
}
// TODO: how to deal with response annotations and async?
return;
}
spanFromRequest = createSpanIfRequestNotHandled(request, spanFromRequest, name, skip);
detachOrCloseSpans(request, response, spanFromRequest, exception);
}
}
这里只贴出doFilter里面的代码, 上面的代码中 需要添加的代码,已经标出来了,仅需把那些代码复制即可 。
获取真实路径的方法
private String getMatchingPattern(HttpServletRequest request) {
// 从request中获取真实路径
String uri = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
if(StringUtils.isEmpty(uri)){
uri = this.urlPathHelper.getPathWithinApplication(request);
}
return (uri) ;
}
从TraceFilter里面拷贝过来的代码,里面有很多依赖类,是不允许别的包访问的, 因此需要将那些类拷贝到自身的包下面,然后改改包路径就可以了
添加配置
// 重写traceFilter.
@Bean
public TraceFilter traceFilter(BeanFactory beanFactory,
SkipPatternProvider skipPatternProvider,WebApplicationContext ctx) {
// 重点
return new TraceLocalFilter(beanFactory, skipPatternProvider.skipPattern(), new HandlerMappingIntrospector(ctx));
}
上面的这个,就是重写TraceFilter,将我们自己写的TraceLocalFilter放入spring容器中,这样后面拦截请求,生成trace的,就是我们自己写的那个类了。 这里名主要是将**new HandlerMappingIntrospector(ctx)**这个对象传进去了
添加依赖配置skipPattern
@Bean
public SkipPatternProvider defaultSkipPatternBean(SleuthWebProperties sleuthWebProperties) {
return defaultSkipPatternProvider(sleuthWebProperties.getSkipPattern());
}
private static SkipPatternProvider defaultSkipPatternProvider(
final String skipPattern) {
return () -> defaultSkipPattern(skipPattern);
}
private static Pattern defaultSkipPattern(String skipPattern) {
if(StringUtils.hasText(skipPattern)){
if(!skipPattern.startsWith("|")){
skipPattern = "|"+skipPattern;
}
return Pattern.compile(SleuthWebProperties.DEFAULT_SKIP_PATTERN+skipPattern);
}
return Pattern.compile(SleuthWebProperties.DEFAULT_SKIP_PATTERN);
}
public interface SkipPatternProvider {
Pattern skipPattern();
}
两步,搞定收工,这里贴的源码不够详细,因为涉及到的源码特别多,根本贴不过来
总结
通过调用spring mvc的HandlerMapping,获取到request中的请求的真实路径,并将这个路径赋予trace收集到的uri, 如果有想要这个解决方案的源码的,请关注微信公众号,并转发一篇公众号文章,截图发到公众号即可!