JAVA Distributed Tracking System Zipkin (III): Brave source Analysis-tracing__java

Source: Internet
Author: User
Tags deprecated log4j

All posts are in the personal independent blog http://blog.mozhu.org starting, welcome to visit.

In the last blog post, we learned about the basic use of the brave framework and analyzed some of the source code associated with tracer. This blog post we'll take a look at the initialization of tracing and the source code of the related class

public class Tracedemo {public

    static void Main (string[] args) {
    Sender Sender = okhttpsender.create ("Http://loc Alhost:9411/api/v2/spans ");
    Asyncreporter asyncreporter = Asyncreporter.builder (sender)
        . Closetimeout (timeunit.milliseconds)
        . Build (SPANBYTESENCODER.JSON_V2);

    Tracing tracing = Tracing.newbuilder ()
        . Localservicename ("Tracer-demo")
        . Spanreporter (asyncreporter)
        . Propagationfactory (Extrafieldpropagation.newfactory (b3propagation.factory, "User-name"))
        . Currenttracecontext (Threadcontextcurrenttracecontext.create ())
        . Build ();
    Tracer Tracer = Tracing.tracer ();
    // ...
    }
}

Brave in the various components to create a large number of Builder design patterns, tacing is no exception, first look at the Tracing.builder tracing.builder

public static final class Tracing.builder {String localservicename;
    Endpoint Localendpoint; Reporter<zipkin2.
    Span> reporter;
    Clock Clock;
    Sampler Sampler = sampler.always_sample;
    Currenttracecontext Currenttracecontext = CurrentTraceContext.Default.inheritable ();
    Boolean traceid128bit = false;
    Boolean supportsjoin = true;

    Propagation.factory propagationfactory = Propagation.Factory.B3;
      Public tracing Build () {if (Clock = = null) Clock = Platform.get ();
        if (Localendpoint = = null) {Localendpoint = Platform.get (). Localendpoint (); 
        if (localservicename!= null) {localendpoint = Localendpoint.tobuilder (). ServiceName (Localservicename). build ();
      } if (reporter = NULL) reporter = Platform.get ();
    Return to New Default (this); } Builder () {}} 

Several important classes of tracing in the process of dependence
- Endpoint -IP, port and application service name information
- Sampler -Sampler, according to the Traceid to determine whether a trace needs to be sampled, that is, reported to Zipkin
- TraceContext -contains traceid,spanid, sampling and other data
- Currenttracecontext -is a helper class that can be used to get the current thread's TraceContext
- Propagation -is an interface that can inject (inject) and extract (extract) data into a data-carrying object carrier
-Factory class of propagation.factory -Propagation

In the previous Tracedemo example, when we initialize the tracing, we set the Localservicename,spanreporter,propagationfactory,currenttracecontext
Where Spanreporter for Asyncreporter we have analyzed its source code in the previous article, as you can see in the build method, the default implementation is platform, the default is to output the span information with logger instead of escalation to Zipkin

  @Override public void Zipkin2. Span span) {
    if (!logger.isloggable (Level.info)) return;
    if (span = null) throw new NullPointerException ("span = null");
    Logger.info (Span.tostring ());
  }
Sampler

Sampler, according to Traceid to determine whether a trace needs to be sampled, that is, escalation to Zipkin

Public abstract class Sampler {public static final sampler always_sample = new Sampler () {@Override public Boolea
    N issampled (Long Traceid) {return true;
    @Override public String toString () {return "alwayssample";

  }
  }; public static Final Sampler never_sample = new Sampler () {@Override public boolean issampled (long Traceid) {RE
    Turn false;
    @Override public String toString () {return "neversample";

  }
  }; /** Returns True if the trace ID should be measured.

  * * Public Abstract Boolean issampled (long Traceid);
   /** * Returns A sampler, given a rate expressed as a percentage.
   * <p>the Sampler returned is good to low volumes of traffic (<100k requests), as it is precise.
   * If you are have high volumes of traffic, consider {@link Boundarysampler}. * * @param rate Minimum sample rate is 0.01, or 1% of traces/public static sampler Create (float rate) {RET Urn Countingsampler.creAte (rate); }
}

Sampler.always_sample always need to be sampled
Sampler.never_sample never sample

Sampler also has an implementation class
Countingsampler can specify the sampling rate, such as countingsampler.create (0.5f), to sample 50% of the requested data, which uses an algorithm that does not expand the analysis. TraceContext

Contains Traceid,spanid, whether to sample data, etc.

There is a section of code in the tracer Newrootcontext method that constructs TraceContext objects through Newbuilder

  TraceContext Newrootcontext (samplingflags samplingflags, list<object> extra) {
    long NextID = Platform.get (). Randomlong ();
    Boolean sampled = Samplingflags.sampled ();
    if (sampled = = null) sampled = Sampler.issampled (NextID);
    Return Tracecontext.newbuilder ()
        . Sampled (sampled)
        . Traceidhigh (traceid128bit? Platform.get (). Nexttraceidhigh (): 0L). Traceid (NextID)
        . Spanid (NextID)
        . Debug (Samplingflags.debug ())
        . Extra (extra). build ();
  

Some of the following properties are in TraceContext
- Traceidhigh -a 16-byte ID that uniquely identifies trace, that is, 128-bit
- Traceid -8-byte ID that uniquely identifies trace
- ParentID -Spanid of the parent span
- Spanid -8-byte ID that uniquely identifies a span in a trace
- Shared -If true, indicates that span information needs to be shared from other tracer
- Extra -additional data set associated with a trace

and two attributes inherited from Samplingflags.
- sampled -sampling
- Debug-Debug-if True, even if sampled is false, it also indicates that trace needs to be sampled (that is, you can overwrite sampled values)

Two interfaces are also defined in the TraceContext injector,extractor

  Public interface Injector<c> {
    void inject (TraceContext TraceContext, C carrier);
  }

  Public interface Extractor<c> {
    tracecontextorsamplingflags extract (C carrier);
  }
Injector-Used to inject various data from the TraceContext into the carrier, where carrier is typically an object that is similar to the HTTP headers that can carry additional information in the RPC middle. Extractor-Used to extract TraceContext-related information or sample tag information in carrier Tracecontextorsamplingflags Tracecontextorsamplingflags

Tracecontextorsamplingflags is a joint type of three types of data, Tracecontext,traceidcontext,samplingflags, the official document says
-When there are Traceid and Spanid, create (TraceContext) is required
-Create when only Spanid is required (traceidcontext)
-In other cases, create (samplingflags) is required
The code in Tracecontextorsamplingflags is simpler, and there is no analysis here Currenttracecontext

Currenttracecontext is a helper class that can be used to get the TraceContext of the current thread, and its default implementation class is Currenttracecontext.default

public static final class default extends Currenttracecontext {static final threadlocal<tracecontext> default =
    New Threadlocal<> (); Inheritable as brave 3 ' s threadlocalserverclientandlocalspanstate was inheritable static final Inheritablethreadloc

    Al<tracecontext> inheritable = new inheritablethreadlocal<> ();

    Final Threadlocal<tracecontext> Local; /** @deprecated prefer {@link #create ()} as it isn ' t inheritable, so can ' t leak contexts.
    * * @Deprecated public Default () {this (inheritable);  }/** Uses a non-inheritable static thread local */public static Currenttracecontext create () {return new
    Default (default);  }/** * Uses an inheritable static thread \ which allows arbitrary calls to {@link * Thread#start ()} to automatically inherit this context.  This is available as it's is feature 3, because some users couldn ' t control threads in their ApplicAtions. * <p>this can be a problem into scenarios such as thread pool expansion, leading to data being * recorded In the wrong span, or spans with the wrong parent.
     If you are are impacted by this, * switch to {@link #create ()}.
    * * public static Currenttracecontext inheritable () {return new Default (inheritable); } Default (Threadlocal<tracecontext> local = if (local = null) throw new NullPointerException ("local = =
      Null ");
    this.local = local;
    @Override public TraceContext get () {return local.get (); }
}

Currenttracecontext.default provides two static methods, that is, create () and inheritable ()
When created using the Create method, the local object is the threadlocal type
When created using the inheritable method, the local object is inheritablethreadlocal type
Threadlocal can be understood as a shared memory space developed by the JVM for the same thread, which is called by different methods in the same thread and can be taken out of the placed object from that space
And when the thread-bound object is fetched using inheritablethreadlocal, the current thread does not, and the current thread's parent thread's shared memory is fetched

Official documentation points out that the inheritable method needs to be used with care in the context of the online pool and may take out the wrong tracecontext, which can cause information such as span to be logged and associated to the wrong Traceid Currenttracecontext.scope

  Public abstract Scope Newscope (@Nullable tracecontext currentspan);

  /** A span remains in the scope it being bound to until the close is called. * * Public
  interface Scope extends Closeable {/** No exceptions are the thrown when
    unbinding a span Scope. * *
    @Ov erride void Close ();
  }

A scope interface is also defined in Currenttracecontext, which inherits from the Closeable interface
Since JDK7, the object that implements the Closeable interface, as long as it is defined in the Try statement, and when finally executes, the JVM will actively call its Close method to reclaim the resource. So the Currenttracecontext provides a newscope method that we can use in the code

Try (Scope scope = Newscope (Invocationcontext)) {
  //do somthing
}

Let's see how the Newscope is realized in Currenttracecontext.default

@Override public Scope newscope (@Nullable tracecontext currentspan) {
      final TraceContext previous = Local.get ();
      Local.set (Currentspan);
      Class Defaultcurrenttracecontextscope implements Scope {
        @Override public void Close () {
          local.set (previous); c7/>}
      return
      new Defaultcurrenttracecontextscope ();
    }

The TraceContext of the current thread is first assigned to the previous variable, and the new TraceContext is set to the current thread, and when the Close method call of scope restores the previous value to the current thread

Use two nested try blocks to demonstrate the meaning of the following procedure

TraceContext TraceContext1;
TraceContext traceContext2;
Try (Scope scope = Newscope (TraceContext1)) {
  //1. Here Currenttracecontext.get () can obtain TRACECONTEXT1
  try (scope Scope = Newscope (traceContext2)) {
  //2. Here Currenttracecontext.get () can get traceContext2
  }
  //3. Here Currenttracecontext.get () can obtain TRACECONTEXT1
}
Before entering the inner-layer try code block, the TRACECONTEXT1 obtained via Currenttracecontext.get () is passed through the Currenttracecontext.get () after entering the inner-layer try code block. The traceContext2 obtained is obtained by Currenttracecontext.get () after running the inner try code block TRACECONTEXT1

This approach is indeed more flexible and elegant, but for the use of people, is also a bit too vague, do not know JDK7 new characteristics of the students just started to see this usage may be a blank face

Of course, this usage must allow the person to use the scope object new in the Try statement, everyone can follow this agreed rules to write, error prone, so currenttracecontext in the provision of several pairs of callable, Wrap method of runnable encapsulation method

  /** wraps the input so this it executes with the same context as now. * Public
  <C> callable<c> Wrap (callable<c> Task) {
    final TraceContext Invocationcontext = Get ();
    Class Currenttracecontextcallable implements callable<c> {
      @Override public C call () throws Exception {
        Try (Scope scope = Newscope (Invocationcontext)) {return
          task.call ();
        }
    }} return new currenttracecontextcallable ();
  }

  /** wraps the input so this it executes with the same context as now. * * Public
  Runnable Wrap (Runnable Task) {
    final TraceContext invocationcontext = Get ();
    Class Currenttracecontextrunnable implements Runnable {
      @Override public void Run () {
        try (scope scope = Newscop E (Invocationcontext)) {
          task.run ();
    }}} return new currenttracecontextrunnable ();
  }

Currenttracecontext also provides packaging methods for executor and Executeservice.

  /** * decorates the input such that the {@link #get () the "Current Trace"} at the "Time a" task is * scheduled is
   Made the task is executed. */Public Executor Executor (Executor delegate) {class Currenttracecontextexecutor implements Executor {@Over
      Ride public void Execute (Runnable task) {Delegate.execute (CurrentTraceContext.this.wrap (Task));
  Return to New Currenttracecontextexecutor (); }/** * decorates the input such that the {@link #get () current trace context} at the time a task is * scheduled
   is made the task is executed. */Public Executorservice Executorservice (Executorservice delegate) {class Currenttracecontextexecutorservice Exten DS Brave.internal.WrappingExecutorService {@Override protected Executorservice delegate () {return Delegat
      E } @Override protected <C> callable<c> Wrap (callable<c> Task) {return Currenttracecontext . thIs.wrap (Task);
      @Override protected Runnable Wrap (Runnable Task) {return CurrentTraceContext.this.wrap (Task);
  Return to New Currenttracecontextexecutorservice (); }

These methods are used in the adorner design pattern, is a more commonly used design pattern, here is no longer to expand the analysis of the threadcontextcurrenttracecontext

You can see in the Tracedemo, we set the Currenttracecontext is Threadcontextcurrenttracecontext.create ()

The Threadcontextcurrenttracecontext is encapsulated for LOG4J2, a class in the Brave-context-log4j2 package, where the Traceid and Spanid two properties are placed in the Threadcontext. We can configure the log print pattern in the log4j2 configuration file, using placeholders%x{traceid} and%x{spanid}, so that each row of logs can print the current Traceid and Spanid

Zipkin-learning\chapter1\servlet25\src\main\resources\log4j2.properties

Appender.console.layout.pattern =%d{absolute} [%x{traceid}/%x{spanid}]%-5p [%t]%c{2}-%m%n

Need to add log-related jars in pom.xml

    <brave.version>4.9.1</brave.version> <log4j.version>2.8.2</log4j.version> <depende Ncy> <groupId>io.zipkin.brave</groupId> &LT;ARTIFACTID&GT;BRAVE-CONTEXT-LOG4J2&LT;/ARTIFACTID&G
      T <version>${brave.version}</version> </dependency> <dependency> <groupid>org.a Pache.logging.log4j</groupid> <artifactId>log4j-core</artifactId> <version>${log4j.ver sion}</version> </dependency> <dependency> <groupid>org.apache.logging.log4j</gro upid> <artifactId>log4j-jul</artifactId> <version>${log4j.version}</version> &L t;/dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifacti d>log4j-jcl</artifactid> <version>${log4j.version}</version> </dependency> <de
  Pendency>    <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>${log4j.version}</version> </dependency>

In the case of Chapter1, if you look at the console of Frontend and backend, the following output 0cabad9917e767ab is Traceid, 0cabad9917e767ab and E96A226CE75D30B4 for Spanid

10:11:05,731 [0cabad9917e767ab/0cabad9917e767ab] INFO  [QTP1441410416-17] servlet. Frontendservlet-frontend Receive Request
10:11:05,820 [0CABAD9917E767AB/E96A226CE75D30B4] INFO  [qtp1441410416-15] servlet. Backendservlet-backend Receive Request
Public final class Threadcontextcurrenttracecontext extends Currenttracecontext {public static Threadcontextcurrenttra
  Cecontext Create () {return Create (CurrentTraceContext.Default.inheritable ()); public static Threadcontextcurrenttracecontext Create (Currenttracecontext delegate) {return new threadcontextcur
  Renttracecontext (delegate);

  Final Currenttracecontext delegate; Threadcontextcurrenttracecontext (Currenttracecontext delegate) {if (delegate = = null) throw new NullPointerException (
    "Delegate = = null");
  This.delegate = delegate;
  @Override public TraceContext get () {return delegate.get (); @Override public Scope newscope (@Nullable TraceContext currentspan) {final String Previoustraceid = Threadcontex
    T.get ("Traceid");

    Final String Previousspanid = Threadcontext.get ("Spanid");
      if (Currentspan!= null) {threadcontext.put ("Traceid", currentspan.traceidstring ()); Threadcontext.put ("Spanid", Hexcodec.tolowerhEx (Currentspan.spanid ()));
      else {threadcontext.remove ("Traceid");
    Threadcontext.remove ("Spanid");
    Scope scope = Delegate.newscope (Currentspan); Class Threadcontextcurrenttracecontextscope implements Scope {@Override public void close () {scope.close ()
        ;
        Threadcontext.put ("Traceid", Previoustraceid);
      Threadcontext.put ("Spanid", Previousspanid);
  Return to New Threadcontextcurrenttracecontextscope (); }
}

Threadcontextcurrenttracecontext inherits Currenttracecontext, overrides its Newscope method, Extracts the Traceid and Spanid in the Currentspan into the Log4j2 context object Threadcontext

Support for SLF4J and log4j can also be found in the Https://github.com/openzipkin/brave/tree/master/context.
The Brave.context.slf4j.MDCCurrentTraceContext in the BRAVE-CONTEXT-SLF4J
The Brave.context.log4j12.MDCCurrentTraceContext in the BRAVE-CONTEXT-LOG4J12
The code is similar, here is not the propagation

Propagation, an English translation propagator, is an interface that can inject (inject) and extract (extract) data into a data-carrying object carrier.
In the case of HTTP protocol, usually carrier means HTTP request object, its HTTP headers can carry trace information, generally HTTP client will inject (inject) trace information in headers, The server side will extract (extract) trace information in the headers
Propagation.setter and Propagation.getter can set and get values in carrier
In addition, injector and extractor methods are returned to Tracecontext.injector and Tracecontext.extractor respectively.

  Interface Setter<c, k> {
    void put (C carrier, K key, String value);
  Interface Getter<c, k> {
    @Nullable String get (C carrier, K key);
  }

  <C> tracecontext.injector<c> Injector (setter<c, k> Setter);
  <C> tracecontext.extractor<c> Extractor (getter<c, k> Getter);

Propagation also has a factory class propagation.factory, there is a factory method created, through Keyfactory to create propagation objects

Abstract class Factory {public
    static final Factory B3 = b3propagation.factory;

    
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.