sleuth+zipkin请求URI路径动态参数-改造方案(十一)

前言

上一节我们了解到,如果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, 如果有想要这个解决方案的源码的,请关注微信公众号,并转发一篇公众号文章,截图发到公众号即可!

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u012394095/article/details/82835484