【弄nèng - Springcloud】Spring-cloud-sleuth Zuul拦截源码解析

spring cloud sleuth的ZUUL源码解析

一. 整体流程

告警模块中处理逻辑涉及到的类及其职责介绍

TracingFilter

  • doFilter 拦截servlet请求进行创建span,之后向下传递执行其他filter,最后结束trace
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = servlet.httpServletResponse(response);

    // Prevent duplicate spans for the same request
    TraceContext context = (TraceContext) request.getAttribute(TraceContext.class.getName());
    if (context != null) {
      // A forwarded request might end up on another thread, so make sure it is scoped
      Scope scope = currentTraceContext.maybeScope(context);
      try {
        chain.doFilter(request, response);
      } finally {
        scope.close();
      }
      return;
    }
	// 创建server端span
    Span span = handler.handleReceive(new HttpServerRequest(httpRequest));

    // Add attributes for explicit access to customization or span context
    request.setAttribute(SpanCustomizer.class.getName(), span.customizer());
    request.setAttribute(TraceContext.class.getName(), span.context());

    Throwable error = null;
    Scope scope = currentTraceContext.newScope(span.context());
    try {
      // any downstream code can see Tracer.currentSpan() or use Tracer.currentSpanCustomizer()
      // 将请求转发给过滤器链下一个filter
      chain.doFilter(httpRequest, httpResponse);
    } catch (IOException | ServletException | RuntimeException | Error e) {
      error = e;
      throw e;
    } finally {
      scope.close();
      // 结束trace
      if (servlet.isAsync(httpRequest)) { // we don't have the actual response, handle later
        servlet.handleAsync(handler, httpRequest, httpResponse, span);
      } else { // we have a synchronous response, so we can finish the span
        // handleSend中调用HttpServerHandler.handleSend, 会判断当前上下文是否还有没有结束的span,如果没有了,则不作任何处理。如果有,则结束该span
        handler.handleSend(servlet.httpServerResponse(httpRequest, httpResponse), error, span);
      }
    }
  }

HttpServerHandler.handleSend

  public void handleSend(@Nullable Resp response, @Nullable Throwable error, Span span) {
    if (response instanceof HttpServerResponse) {
      HttpServerResponse.Adapter adapter =
        new HttpServerResponse.Adapter((HttpServerResponse) response);
      handleFinish(adapter, adapter.unwrapped, error, span);
    } else {
      handleFinish(adapter, response, error, span);
    }
  }

RealSpan.finish
span 结束逻辑

  @Override public void finish(long timestamp) {
    if (!pendingSpans.remove(context)) return;
    synchronized (state) {
    // 设置结束时间戳
      state.finishTimestamp(timestamp);
    }
    // 添加tags
    finishedSpanHandler.handle(context, state);
  }

HttpHandler.finishInNullScope
关闭span

/** Clears the scope to prevent remote reporters from accidentally tracing */
  void finishInNullScope(Span span, long timestamp) {
    Scope ws = currentTraceContext.maybeScope(null);
    try {
      if (timestamp == 0L) {
        span.finish();
      } else {
        span.finish(timestamp);
      }
    } finally {
      ws.close();
    }
  }

ZipkinFinishedSpanHandler.handle
Reporter上报span

  @Override public boolean handle(TraceContext context, MutableSpan span) {
    if (!alwaysReportSpans && !Boolean.TRUE.equals(context.sampled())) return true;

    Span.Builder builderWithContextData = Span.newBuilder()
      .traceId(context.traceIdString())
      .parentId(context.parentIdString())
      .id(context.spanIdString());
    if (context.debug()) builderWithContextData.debug(true);

    converter.convert(span, builderWithContextData);
    spanReporter.report(builderWithContextData.build());
    return true;
  }

TracingProtocolExec

httpclient 拦截器,用于创建client span,结束client span

 @Override public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request,
    HttpClientContext clientContext, HttpExecutionAware execAware)
    throws IOException, HttpException {
    Span span = handler.nextSpan(new HttpClientRequest(request));
    HttpClientResponse response = null;
    Throwable error = null;
    try (SpanInScope ws = tracer.withSpanInScope(span)) {
      CloseableHttpResponse result = protocolExec.execute(route, request, clientContext, execAware);
      response = new HttpClientResponse(result);
      return result;
    } catch (IOException | HttpException | RuntimeException | Error e) {
      error = e;
      throw e;
    } finally {
      // 结束client span
      handler.handleReceive(response, error, span);
    }
  }

TracingMainExec

真正的执行http请求,网关转发到后端服务,默认使用httpclient,可选okHttpclient等。

@Override public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request,
    HttpClientContext context, HttpExecutionAware execAware)
    throws IOException, HttpException {
    Span span = tracer.currentSpan();
    if (span != null) {
      handler.handleSend(new TracingProtocolExec.HttpClientRequest(request), span);
    }
    CloseableHttpResponse response = mainExec.execute(route, request, context, execAware);
    if (span != null) {
      if (isRemote(context, span)) {
        if (serverName != null) span.remoteServiceName(serverName);
        // 为span设置远程remoteIpAndPort
        HttpAdapter.parseTargetAddress(request, span);
      } else {
        span.kind(null); // clear as cache hit
      }
    }
    return response;
  }

TracePostZuulFilter

zuul拦截器,type=post, order=0,所以在转发请求之后最先执行

	@Override
	public Object run() {
		if (log.isDebugEnabled()) {
			log.debug("Marking current span as handled");
		}
		HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
		Throwable exception = RequestContext.getCurrentContext().getThrowable();
		Span currentSpan = this.tracer.currentSpan();
		// 结束server span
		this.handler.handleSend(response, exception, currentSpan);
		if (log.isDebugEnabled()) {
			log.debug("Handled send of " + currentSpan);
		}
		return null;
	}

后续

那么为什么sleuth能拦截httpclient呢。因为这个配置,默认每次使用HttpClientBuilder都会返回TracingHttpClientBuilder对象。而brave对TracingHttpClientBuilder进行了拦截处理、
在这里插入图片描述


项目推荐

IT-CLOUD :IT服务管理平台,集成基础服务,中间件服务,监控告警服务等。
IT-CLOUD-ACTIVITI6 :Activiti教程源码。博文在本CSDN Activiti系列中。
IT-CLOUD-ELASTICSEARCH :elasticsearch教程源码。博文在本CSDN elasticsearch系列中。
IT-CLOUD-KAFKA :spring整合kafka教程源码。博文在本CSDN kafka系列中。
IT-CLOUD-KAFKA-CLIENT :kafka client教程源码。博文在本CSDN kafka系列中。

开源项目,持续更新中,喜欢请 Star~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值