zipKin链路调用追踪

具体的链路调用追踪原理及实现这里就不说了,可以自行百度,这里简单记录下看源码的理解,方便日后回忆。

首先介绍个概念,traceId和Span就不说了,除了这两个还有一个annotation:
Annotation: 注解,用来记录请求特定事件相关信息(例如时间),通常包含四个注解信息
cs - Client Start,表示客户端发起请求
sr - Server Receive,表示服务端收到请求
ss - Server Send,表示服务端完成处理,并将结果发送给客户端
cr - Client Received,表示客户端获取到服务端返回信息

public class Annotation implements Serializable {
static final long serialVersionUID = 1L;
public final long timestamp;
public final String value;   //取值为cs、sr、ss、cr
public final Endpoint host;
... ...
}



SpanCollector类(通过它将span信息发送到zipKin):

(1)、collect(Span):将Span信息放入BlockingQueue
(2)、send(JsonString[span的list]):将Span信息通过Http发送到zipKin进行处理显示。
当然步骤2不能影响业务时间,所以是定义了一个定时任务newScheduledThreadPool(1)将BlockingQueue中的Span拿出来并调用send方法。

其中Span中属性简略如下:

public class Span implements Serializable {
static final long serialVersionUID = 1L;
public volatile Long startTick;
private long trace_id;  *
private String name;
private long id;        *
private Long parent_id; *
private List annotations = Collections.emptyList();
private List binary_annotations = Collections.emptyList();
private Boolean debug;
private Long timestamp;
private Long duration;
... ...
}



zipKin的主要设计就是说在server接受请求和发送请求时使用拦截器和过滤器进行前后处理,处理的过程就是将该Span的信息记录下来并在ss和cr的时候将Span信息发送到zipKin服务器进行延时数据的整理。过程中最主要的就是Brave类,前面使用的拦截器类和处理类都是在Brave类中:

//就是Tracer和Interceptor(Filter)两个
public class Brave {
private final ServerTracer serverTracer;
private final ClientTracer clientTracer;
private final LocalTracer localTracer;
private final ServerRequestInterceptor serverRequestInterceptor;
private final ServerResponseInterceptor serverResponseInterceptor;
private final ClientRequestInterceptor clientRequestInterceptor;
private final ClientResponseInterceptor clientResponseInterceptor;
private final AnnotationSubmitter serverSpanAnnotationSubmitter;
private final ServerSpanThreadBinder serverSpanThreadBinder;
private final ClientSpanThreadBinder clientSpanThreadBinder;



所以具体的就是在server接受的请求上使用拦截器并设置sr和ss,在server发出的请求上使用拦截器设置cs和cr,下面以处理一个server的Span为例介绍下接收请求时拦截器做了哪些事:

使用拦截器后(实际调用的即ServerRequestInterceptor.handle):
1、Filter.handle(... ...):创建一个Span(这里的Span信息需要保存在ThreadLocal中)、调用Tracer.setServerReceived保存当前的span及annotation信息。
2、doFilter()
3、Filter.handle(... ... ):调用Tracer.setServerSend方法计算延时(现在的时间即ss-Span中annotion中保存的时间即sr)并调用SpanCollector.collect。

而在server发送请求时拦截器会将spanId和traceId写进请求头,这样下一个Server就可以获得traceId和parentId:

this.request.addHeader(BraveHttpHeaders.Sampled.getName(), "1");
this.request.addHeader(BraveHttpHeaders.TraceId.getName(), IdConversion.convertToString(spanId.traceId));
this.request.addHeader(BraveHttpHeaders.SpanId.getName(), IdConversion.convertToString(spanId.spanId));



总的来说在使用zipKin的时候主要就是要在项目中添加需要用到的几个功能Bean:

@Configuration
public class ZipkinConfig {

@Autowired
private ZipkinProperties properties;


@Bean
public SpanCollector spanCollector() {
    HttpSpanCollector.Config config = HttpSpanCollector.Config.builder().connectTimeout(properties.getConnectTimeout()).readTimeout(properties.getReadTimeout())
            .compressionEnabled(properties.isCompressionEnabled()).flushInterval(properties.getFlushInterval()).build();
    return HttpSpanCollector.create(properties.getUrl(), config, new EmptySpanCollectorMetricsHandler());
}


@Bean
public Brave brave(SpanCollector spanCollector){
    Brave.Builder builder = new Brave.Builder(properties.getServiceName());  //指定state
    builder.spanCollector(spanCollector);
    builder.traceSampler(Sampler.ALWAYS_SAMPLE);
    Brave brave = builder.build();
    return brave;
}

//该fliter用来处理Server接受到的请求
@Bean
public BraveServletFilter braveServletFilter(Brave brave){
    BraveServletFilter filter = new BraveServletFilter(brave.serverRequestInterceptor(),brave.serverResponseInterceptor(),new DefaultSpanNameProvider());
    return filter;
}

//该Filter用来处理server发出的请求,所以项目中发送请求时需要使用该OkHttpClient
@Bean
public OkHttpClient okHttpClient(Brave brave){
    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(new BraveOkHttpRequestResponseInterceptor(brave.clientRequestInterceptor(), brave.clientResponseInterceptor(), new DefaultSpanNameProvider()))
            .build();
    return client;
}
}



至于zipKin服务器接收到所有的span信息后怎么整理并显示UI还没详细看,上面属于zipKin客户端的功能,zipKin服务端主要是对trace信息进行整理、存储、显示等,trace信息默认是存储在内存中,也可以选择存储在mysql数据库中等,另外在业务流量大的时候客户端可以将trace信息写进kafka,然后zipKin Server端再从kafka中拉取信息。

你可能感兴趣的:(zipKin链路调用追踪)