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);

原文地址:https://www.cnblogs.com/IC1101/p/13970006.html