• zipkin


    Brave核心lib

        Brave是一个lib用于捕获,以及上报关于分布式操作的性能的信息到Zipkin。大多数用户不会直接时候Brave,而是把应用Brave到对应的框架上;

        这个模块包含tracer(可以用来创建,并加入span),当然也包含一个lib去在不同的网络边界传播trace context(上下文),比如说http headers。







    Setup

        第一步,我们要配置一下,如何发送到Zipkin上。
    如下是一个如何通过HTTP发送trace数据到Zipkin的一个样例:

    
    // Configure a reporter, which controls how often spans are sent
    //   (this dependency is io.zipkin.reporter2:zipkin-sender-okhttp3)
    sender = OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans");
    //   (this dependency is io.zipkin.reporter2:zipkin-reporter-brave)
    zipkinSpanHandler = AsyncZipkinSpanHandler.create(sender);
    
    // Create a tracing component with the service name you want to see in Zipkin.
    // 可以想见,一个tracing对应一个服务
    tracing = Tracing.newBuilder()
                     .localServiceName("my-service")
                     .addSpanHandler(zipkinSpanHandler)
                     .build();
    
    // Tracing exposes objects you might need, most importantly the tracer
    // Tracing 暴露提供你需要的对象,最终要的是Tracer,tracer存在ThreadLocal中?
    tracer = tracing.tracer();
    
    // Failing to close resources can result in dropped spans! When tracing is no
    // longer needed, close the components you made in reverse order. This might be
    // a shutdown hook for some users.
    tracing.close();
    // tracing 应该是一个链路结束是关闭,ZipkinSpanHandler 和 sender 应该是系统结束前关闭!
    zipkinSpanHandler.close();
    sender.close();
    
    




    Tracing

         tracer创建并加入span。tracer可以通过设置采样率来控制发送到Zipkin的数据量。
         tracer完成发送数据到Zipkin后,会返回Span。当开启一个span,你可以这个span添加关心的event和tag;
         span包含context,可以用来标识它在分布式操作中的位置

    
    // Start a new trace or a span within an existing trace representing an operation
    ScopedSpan span = tracer.startScopedSpan("encode");
    try {
      // The span is in "scope" meaning downstream code such as loggers can see trace IDs
      return encoder.encode();
    } catch (RuntimeException | Error e) {
      span.error(e); // Unless you handle exceptions, you might not know the operation failed!
      throw e;
    } finally {
      span.finish(); // always finish the span
    }
    
    
    
    // Start a new trace or a span within an existing trace representing an operation
    Span span = tracer.nextSpan().name("encode").start();
    // Put the span in "scope" so that downstream code such as loggers can see trace IDs
    try (SpanInScope ws = tracer.withSpanInScope(span)) {
      return encoder.encode();
    } catch (RuntimeException | Error e) {
      span.error(e); // Unless you handle exceptions, you might not know the operation failed!
      throw e;
    } finally {
      span.finish(); // note the scope is independent of the span. Always finish a span.
    }
    
    

        如上两个示例,作用是完全相同的,生成的span要么是一个全新的root span,要么是一个已存在的trace的子span;







    定制span

         一旦你获取到了span,你可以给span添加tag,tag可以很好的提现span的特性。比如说你可以给你的span添加你的运行时版本;

    span.tag("clnt/finagle.version", "6.36.0");
    

         当你蛮对各种各样的Span时,Tag类会对你有帮助。对于通用的或者重量级的tag,尝试使用Tag来定义并使用;
    
    SUMMARY_TAG = new Tag<Summarizer>("summary") {
      @Override protected String parseValue(Summarizer input, TraceContext context) {
        return input.computeSummary();
      }
    }
    
    // This works for any variant of span
    SUMMARY_TAG.tag(summarizer, span);
    
    

         当期望暴露自定义span的能力给第三方时,brave.SpanCustomizer会是比brave.Span更好的选择。前者更易于理解和测试,并且不会诱导用户去操作span的lifecycle hooks。
    
    interface MyTraceCallback {
      void request(Request request, SpanCustomizer customizer);
    }
    
    
    for (MyTraceCallback callback : userCallbacks) {
      callback.request(request, span);
    }
    
    
    
    // Some DI configuration wires up the current span customizer
    @Bean SpanCustomizer currentSpanCustomizer(Tracing tracing) {
      return CurrentSpanCustomizer.create(tracing);
    }
    
    // user code can then inject this without a chance of it being null.
    @Inject SpanCustomizer span;
    
    void userCode() {
      span.annotate("tx.started");
      ...
    }
    




    #### Remote spans      Remote spans是指,同一个trace涉及到不同进程(服务),需要在进程间传递上下文信息;作为调用方,你通常需要: 1. 启用一个span,并加trace信息添加到request中 2. 把新的span放进到新的scope中 3. 指定调用 4. 捕获任何异常 5. 完结span
    
    requestWrapper = new ClientRequestWrapper(request);
    span = tracer.nextSpan(sampler, requestWrapper); // 1.
    tracing.propagation().injector(ClientRequestWrapper::addHeader)
                         .inject(span.context(), requestWrapper);
    span.kind(request.spanKind());
    span.name("Report");
    span.start();
    try (Scope ws = currentTraceContext.newScope(span.context())) { // 2.
      return invoke(request); // 3.
    } catch (Throwable e) {
      span.error(error); // 4.
      throw e;
    } finally {
      span.finish(); // 5.
    }
    
    




    Sampling

        Sampling可以减少被采样的span数,不采样的span标记为no overhead(noop);


        是否采样是一个up-front决定,意味着,是否采样是在trace第一次操作的时候就被决定的,并且这个决定会一直向下传播到整个trace;也就是一个trace要么整个被采样,要么整个不被采样;
    你可以象如下一样自定义自己的sample策略:

    
    tracing = Tracing.newBuilder()
                     .sampler(RateLimitingSampler.create(10))
    --snip--
                     .build();
    
    --------------------------------------------------------------------
    
    SamplerFunction<Request> requestBased = (request) -> {
      if (request.url().startsWith("/experimental")) {
        return true;
      } else if (request.url().startsWith("/static")) {
        return false;
      }
      return null;
    };
    
    Span nextSpan(final Request input) {
      return tracer.nextSpan(requestBased, input);
    }
    
    

    有些人可能希望基于java方法上的注释来决定是否采样。

    
    // derives a sample rate from an annotation on a java method
    SamplerFunction<Traced> samplerFunction = DeclarativeSampler.createWithRate(Traced::sampleRate);
    
    @Around("@annotation(traced)")
    public Object traceThing(ProceedingJoinPoint pjp, Traced traced) throws Throwable {
      // When there is no trace in progress, this overrides the decision based on the annotation
      ScopedSpan span = tracer.startScopedSpan(spanName(pjp), samplerFunction, traced);
      try {
        return pjp.proceed();
      } catch (RuntimeException | Error e) {
        span.error(e);
        throw e;
      } finally {
        span.finish();
      }
    }
    
    




    Baggage

        有时候,你需要传递额外的字段;比如说,你需要知道一个具体请求的countryCode,你能通过HTTP头来传递;

    
    import brave.baggage.BaggagePropagationConfig.SingleBaggageField;
    
    // Configure your baggage field
    COUNTRY_CODE = BaggageField.create("country-code");
    
    // When you initialize the builder, add the baggage you want to propagate
    tracingBuilder.propagationFactory(
      BaggagePropagation.newFactoryBuilder(B3Propagation.FACTORY)
                        .add(SingleBaggageField.remote(COUNTRY_CODE))
                        .build()
    );
    
    // Later, you can retrieve that country code in any of the services handling the trace
    // and add it as a span tag or do any other processing you want with it.
    countryCode = COUNTRY_CODE.getValue(context);
    
    

        只要字段是通过BaggagePropagation配置的,就可以本地区读取或者更新;
    
    COUNTRY_CODE.updateValue("FO");
    String countryCode = COUNTRY_CODE.getValue();
    
    //or
    
    COUNTRY_CODE.updateValue(span.context(), "FO");
    String countryCode = COUNTRY_CODE.get(span.context());
    Tags.BAGGAGE_FIELD.tag(COUNTRY_CODE, span);
    
    
  • 相关阅读:
    洛谷P2661: 信息传递(图的遍历)
    洛谷P1305: 新二叉树
    洛谷 P1030 :求先序排列
    POJ 3041:Asteroids(二分图最大匹配)
    洛谷P2774 :方格取数问题( 网络流24题 奇偶建图+最小割)
    hdu 3061:Battle(最大权闭合图)
    hdu 1532:Drainage Ditches(Dinic算法)
    洛谷P1345: [USACO5.4]奶牛的电信Telecowmunication(拆点+最小割)
    hihoCoder1121 : 二分图一•二分图判定
    (转载)javascript客户端生成MD5值的函数代码
  • 原文地址:https://www.cnblogs.com/IC1101/p/13970006.html
Copyright © 2020-2023  润新知