一、实现思路

1、过滤器实现思路

所有调用链数据都通过过滤器实现埋点并收集、同一条链共享一个traceId、每个节点有唯一的spanId。

2、共享传递方式

1、rpc调用:通过隐式传参、dubbo有提供spi在rpc调用之前塞到请求中。参考:dubbo系列六、SPI扩展Filter隐式传参

2、http调用:通过servlet过滤器、在请求前放入requestHead中传递、resTemplate也是如此。

参考:调用链二、Zipkin 和 Brave 实现(springmvc、RestTemplate)服务调用跟踪

3、redis和DB等调用:原理相似,可以通过aop在调用前后拦截。

4、业务代码侵入低。

二、架构图

三、业务项目接入starter步骤

1、pom.xml添加maven包

<dependency>
<groupId>trace.starter</groupId>
<artifactId>trace-starter</artifactId>
<version>1.0.-SNAPSHOT</version>
</dependency>

2、application.yml添加trace配置

dubbo.trace:
enabled: true
connectTimeout:
readTimeout:
flushInterval:
compressionEnabled: true
applicationName: dubbo-zipkin0
zipkinUrl: http://192.168.1.100:9411

3、在springboot开启注解

@EnableTraceAutoConfigurationProperties,例如:

package com.example.demo;
import com.dubbo.trace.configuration.EnableTraceAutoConfigurationProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
@EnableTraceAutoConfigurationProperties
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

四、实现代码

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.</modelVersion> <groupId>trace.starter</groupId>
<artifactId>trace-starter</artifactId>
<version>1.0.-SNAPSHOT</version> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4..RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--zipkin-brave start-->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-core</artifactId>
<version>3.9.</version>
</dependency>
<!--http方式收集-->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-spancollector-http</artifactId>
<version>3.9.</version>
</dependency>
<!--zipkin-brave end--> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.</version>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.</version>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency> <!--logback日志-->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.8</version>
</dependency> <dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>1.2</version>
<scope>provided</scope>
</dependency> <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency> </dependencies>
</project>

1、base过滤器、过滤器父类

package com.dubbo.trace.base;

import com.dubbo.trace.TraceContext;
import com.dubbo.trace.send.TraceAgent;
import com.dubbo.trace.utils.IdUtils;
import com.dubbo.trace.utils.NetworkUtils;
import com.twitter.zipkin.gen.Annotation;
import com.twitter.zipkin.gen.BinaryAnnotation;
import com.twitter.zipkin.gen.Endpoint;
import com.twitter.zipkin.gen.Span;
import net.logstash.logback.encoder.org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpRequest; import javax.servlet.http.HttpServletRequest; public abstract class BaseFilter { /**
* 创建span信息
*/
protected Span createSpan() {
Span span = new Span(); long id = IdUtils.getId();
span.setId(id); Long traceId = TraceContext.getTraceId();
// 首次调用
if (traceId == null) {
TraceContext.start();
traceId = id;
TraceContext.setTraceId(traceId);
} span.setTrace_id(traceId);
span.setName(TraceContext.getTraceConfig().getApplicationName()); // 首次调用spanId和parentId相等
if (TraceContext.getSpanId() == null) {
span.setParent_id(span.getId());
} span.setParent_id(TraceContext.getSpanId());
TraceContext.setSpanId(span.getId()); return span;
} /**
* 添加节点信息
*/
public void addToAnnotations(Span span, String traceType, Long timeStamp) {
span.addToAnnotations(
Annotation.create(timeStamp, traceType,
Endpoint.create(TraceContext.getTraceConfig().getApplicationName(),
NetworkUtils.getIp(),
TraceContext.getTraceConfig().getServerPort()))
);
} /**
* 增加接口信息
*/
protected void addToBinary_annotations(Span span, String key, String value) {
span.addToBinary_annotations(BinaryAnnotation.create(key, value,
Endpoint.create(TraceContext.getTraceConfig().getApplicationName(),
NetworkUtils.getIp(),
TraceContext.getTraceConfig().getServerPort())));
} /**
* 结束调用链
*/
public void endTrace(Span span, Long duration, String traceType) {
addToAnnotations(span, traceType, System.currentTimeMillis() * );
span.setDuration(duration);
TraceAgent.getTraceAgent().send(span);
} protected void getTraceHttpHeader(HttpServletRequest httpReq) { String traceId = httpReq.getHeader("trace_id");
String spanId = httpReq.getHeader("span_id"); if (StringUtils.isNotBlank(traceId)) {
TraceContext.setTraceId(Long.parseLong(traceId));
} if (StringUtils.isNotBlank(spanId)) {
TraceContext.setSpanId(Long.parseLong(spanId));
}
} protected void setTraceToHttpHeader(HttpRequest httpRequest, Span span) {
// 内部请求可以携带trace信息,外部请求改行代码注释掉
httpRequest.getHeaders().set("trace_id", String.valueOf(span.getTrace_id()));
httpRequest.getHeaders().set("span_id", String.valueOf(span.getId()));
} }

2、dubbo消费者过滤器:TraceConsumerFilter.java

package com.dubbo.trace.dubbo;

import com.alibaba.dubbo.rpc.*;
import com.dubbo.trace.TraceContext;
import com.dubbo.trace.base.BaseFilter;
import com.twitter.zipkin.gen.Span;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch; import java.util.Arrays;
import java.util.Map; /**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月25日
* @since 1.0
*/ @Component
public class TraceConsumerFilter extends BaseFilter implements Filter { @Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// 开启调用链
Span span = this.startTrace(invoker, invocation);
StopWatch stopWatch = new StopWatch();
stopWatch.start(); // 远程调用
Result result = invoker.invoke(invocation); // 结束调用链
stopWatch.stop();
// 记录出参
addToBinary_annotations(span, "results", result.getValue().toString());
this.endTrace(span, stopWatch.getTotalTimeMillis() * , TraceContext.ANNO_CR);
return result;
} protected Span startTrace(Invoker<?> invoker, Invocation invocation) {
Span span = createSpan(); Long timeStamp = System.currentTimeMillis() * ;
span.setTimestamp(timeStamp);
span.setName("RPC:" + invoker.getInterface().getSimpleName() + ":" + invocation.getMethodName()); addToAnnotations(span, TraceContext.ANNO_CS, timeStamp); Map<String, String> attaches = invocation.getAttachments();
attaches.put(TraceContext.TRACE_ID_KEY, String.valueOf(span.getTrace_id()));
attaches.put(TraceContext.SPAN_ID_KEY, String.valueOf(span.getId())); // 记录入参
addToBinary_annotations(span, "params", Arrays.toString(invocation.getArguments()));
return span;
}
}

3、dubbo生产者过滤器:TraceProviderFilter.java

package com.dubbo.trace.dubbo;

import com.alibaba.dubbo.rpc.*;
import com.dubbo.trace.TraceContext;
import com.dubbo.trace.base.BaseFilter;
import com.twitter.zipkin.gen.Span;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch; import java.util.Arrays; /**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月25日
* @since 1.0
*/ @Component
public class TraceProviderFilter extends BaseFilter implements Filter { @Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { // 开启调用链
Span span = this.startTrace(invoker, invocation);
StopWatch stopWatch = new StopWatch();
stopWatch.start(); // 远程调用
Result result = invoker.invoke(invocation); // 结束调用链
stopWatch.stop();
// 记录出参
this.addToBinary_annotations(span, "results", result.getValue().toString());
this.endTrace(span, stopWatch.getTotalTimeMillis() * , TraceContext.ANNO_SS); return result;
} protected Span startTrace(Invoker<?> invoker, Invocation invocation) {
Long traceId = Long.valueOf(invocation.getAttachment(TraceContext.TRACE_ID_KEY));
Long spanId = Long.valueOf(invocation.getAttachment(TraceContext.SPAN_ID_KEY)); TraceContext.setTraceId(traceId);
TraceContext.setSpanId(spanId); Span span = createSpan(); Long timeStamp = System.currentTimeMillis() * ;
span.setTimestamp(timeStamp);
addToAnnotations(span, TraceContext.ANNO_SR, timeStamp); span.setName("RPC:" + invoker.getInterface().getSimpleName() + ":" + invocation.getMethodName()); // 记录入参
addToBinary_annotations(span, "params", Arrays.toString(invocation.getArguments())); return span;
} }

4、RestTemplate过滤器:RestTemplateFilter.java

package com.dubbo.trace.restTemplate;

import com.dubbo.trace.TraceContext;
import com.dubbo.trace.base.BaseFilter;
import com.dubbo.trace.utils.NetworkUtils;
import com.twitter.zipkin.gen.BinaryAnnotation;
import com.twitter.zipkin.gen.Endpoint;
import com.twitter.zipkin.gen.Span;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StopWatch; import java.io.IOException; public class RestTemplateFilter extends BaseFilter implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] body, ClientHttpRequestExecution execution) throws IOException {
// 开启调用链
ClientHttpResponse response = null;
Span span = this.startTrace(httpRequest);
StopWatch stopWatch = new StopWatch();
stopWatch.start(); try {
// 内部请求头可以携带trace信息,外部请求改行代码注释掉
this.setTraceToHttpHeader(httpRequest, span);
// 保证请求继续被执行
response = execution.execute(httpRequest, body); // 结束调用链
this.endTrace(span, stopWatch.getTotalTimeMillis() * , TraceContext.ANNO_CR);
} catch (Exception e) {
// 异常记录到调用链
span.addToBinary_annotations(BinaryAnnotation.create("error", e.getMessage(),
Endpoint.create(TraceContext.getTraceConfig().getApplicationName(),
NetworkUtils.getIp(),
TraceContext.getTraceConfig().getServerPort())));
this.endTrace(span, stopWatch.getTotalTimeMillis() * , TraceContext.ANNO_CR);
e.printStackTrace();
} return response;
} private Span startTrace(HttpRequest httpRequest) {
Span span = createSpan();
Long timeStamp = System.currentTimeMillis() * ;
span.setTimestamp(timeStamp);
span.setName("restTemplate:" + httpRequest.getURI() + ":" + httpRequest.getMethod());
addToAnnotations(span, TraceContext.ANNO_CS, timeStamp);
return span;
} }

5、Servlet过滤器:TraceServletFilter.java

package com.dubbo.trace.servlet;

import com.dubbo.trace.TraceContext;
import com.dubbo.trace.base.BaseFilter;
import com.dubbo.trace.utils.NetworkUtils;
import com.twitter.zipkin.gen.BinaryAnnotation;
import com.twitter.zipkin.gen.Endpoint;
import com.twitter.zipkin.gen.Span;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; @Slf4j
@Component
public class TraceServletFilter extends BaseFilter implements Filter { public TraceServletFilter() {
} public void destroy() {
} @Override
public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) req;
BufferedHttpRequestWrapper newReq = new BufferedHttpRequestWrapper(httpReq); Span span = this.startTrace(newReq);
Long timeStamp = System.currentTimeMillis() * ;
span.setTimestamp(timeStamp);
addToAnnotations(span, TraceContext.ANNO_SR, timeStamp);
StopWatch stopWatch = new StopWatch();
stopWatch.start(); try {
chain.doFilter(newReq, resp);
stopWatch.stop();
} catch (Throwable var15) {
span.addToBinary_annotations(BinaryAnnotation.create("error", var15.getMessage(),
Endpoint.create(NetworkUtils.getIp() + ":" + TraceContext.getTraceConfig().getServerPort() + httpReq.getRequestURL().toString(),
NetworkUtils.getIp(),
TraceContext.getTraceConfig().getServerPort())));
this.endTrace(span, stopWatch.getTotalTimeMillis() * , TraceContext.ANNO_SS);
throw var15;
} finally {
HttpServletResponse var12 = (HttpServletResponse) resp;
var12.setHeader("trace_id", String.valueOf(span.getTrace_id()));
var12.setHeader("span_id", String.valueOf(span.getId()));
this.endTrace(span, stopWatch.getTotalTimeMillis() * , TraceContext.ANNO_SS);
} } public Span startTrace(HttpServletRequest httpReq) {
// 处理HTTP头部trace信息
getTraceHttpHeader(httpReq);
Span span = createSpan();
span.setName("HTTP:" + NetworkUtils.ip + ":" + TraceContext.getTraceConfig().getServerPort() + httpReq.getRequestURI()); // cookies
// addToBinary_annotations(span,"cookies",Arrays.toString(httpReq.getCookies()));
return span;
} }

6、BufferedHttpRequestWrapper.java

package com.dubbo.trace.servlet;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*; public class BufferedHttpRequestWrapper extends HttpServletRequestWrapper {
private static final Logger log = LoggerFactory.getLogger(BufferedHttpRequestWrapper.class);
private byte[] reqBody = null; public BufferedHttpRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.reqBody = IOUtils.toString(request.getInputStream(), "utf-8").getBytes();
} @Override
public ServletInputStream getInputStream() throws IOException {
return new MyServletInputStream(new ByteArrayInputStream(this.reqBody)); } public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
} public String getRequestBody() throws UnsupportedEncodingException {
return new String(this.reqBody, "utf-8");
} class MyServletInputStream extends ServletInputStream {
private InputStream inputStream; public MyServletInputStream(InputStream inputStream) {
this.inputStream = inputStream;
} @Override
public int read() throws IOException {
return inputStream.read();
} @Override
public boolean isFinished() {
return false;
} @Override
public boolean isReady() {
return false;
} @Override
public void setReadListener(ReadListener readListener) { }
} }

7、TraceContext.java 上下文

用于存储共享的traceId和spanId到InheritableThreadLocal中(子线程也能获取到、而ThreadLocal不能)

package com.dubbo.trace;

import com.dubbo.trace.configuration.TraceConfig;
import com.twitter.zipkin.gen.Span;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List; /**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月25日
* @since 1.0
*/
@Data
@Component
public class TraceContext {
public static final String TRACE_ID_KEY = "traceId";
public static final String SPAN_ID_KEY = "spanId";
public static final String ANNO_CS = "cs";
public static final String ANNO_CR = "cr";
public static final String ANNO_SR = "sr";
public static final String ANNO_SS = "ss";
public static TraceContext traceContext;
private static ThreadLocal<Long> TRACE_ID = new InheritableThreadLocal<>();
private static ThreadLocal<Long> SPAN_ID = new InheritableThreadLocal<>();
private static ThreadLocal<List<Span>> SPAN_LIST = new InheritableThreadLocal<>();
@Autowired
private TraceConfig traceConfig; public static Long getSpanId() {
return SPAN_ID.get();
} public static void setSpanId(Long spanId) {
SPAN_ID.set(spanId);
} public static Long getTraceId() {
return TRACE_ID.get();
} public static void setTraceId(Long traceId) {
TRACE_ID.set(traceId);
} public static void clear() {
TRACE_ID.remove();
SPAN_ID.remove();
SPAN_LIST.remove();
} public static void start() {
clear();
SPAN_LIST.set(new ArrayList<Span>());
} public static TraceConfig getTraceConfig() {
return traceContext.traceConfig;
} public static void addSpan(Span span) {
List<Span> spanList = SPAN_LIST.get();
spanList.add(span);
} @PostConstruct
public void init() {
traceContext = this;
}
}

8、工具类用户生成、traceId和spanId、获取本机IP

IdUtils.java

package com.dubbo.trace.utils;

import java.util.Random;

/**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月25日
* @since 1.0
*/
public class IdUtils {
public static Long getId() {
return NetworkUtils.getIp() + System.currentTimeMillis();
}
}

NetworkUtils.java

package com.dubbo.trace.utils;

import java.net.InetAddress;
import java.net.UnknownHostException; /**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月29日
* @since 1.0
*/
public class NetworkUtils {
public static String ip;
private static InetAddress addr; static {
try {
addr = InetAddress.getLocalHost();
ip = addr.getHostAddress().toString(); //获取本机ip
} catch (UnknownHostException e) {
e.printStackTrace();
}
} public static int ipToLong(String strIp) {
int[] ip = new int[];
//先找到IP地址字符串中.的位置  
int position1 = strIp.indexOf(".");
int position2 = strIp.indexOf(".", position1 + );
int position3 = strIp.indexOf(".", position2 + );
//将每个.之间的字符串转换成整型  
ip[] = Integer.parseInt(strIp.substring(, position1));
ip[] = Integer.parseInt(strIp.substring(position1 + , position2));
ip[] = Integer.parseInt(strIp.substring(position2 + , position3));
ip[] = Integer.parseInt(strIp.substring(position3 + ));
return (ip[] << ) + (ip[] << ) + (ip[] << ) + ip[];
} public static int getIp() {
return ipToLong(ip);
}
}

9、收集代理和发送实现类

目前发送http请求、后续可扩展发送MQ

SendHttp.java

package com.dubbo.trace.send;

import com.github.kristofa.brave.http.HttpSpanCollector;
import com.twitter.zipkin.gen.Span;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component; @Component
@DependsOn("zipkinConfig")
public class SendHttp extends TraceAgent { @Autowired
private HttpSpanCollector httpSpanCollector; @Override
public void send(Span span) {
if (span != null) {
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println(span.toString());
httpSpanCollector.collect(span);
httpSpanCollector.flush();
}
});
}
} }

TraceAgent.java

package com.dubbo.trace.send;

import com.twitter.zipkin.gen.Span;

import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory; /**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月25日
* @since 1.0
*/
public abstract class TraceAgent {
private static TraceAgent traceAgent;
private final int THREAD_POOL_COUNT = ;
protected final ExecutorService executor =
Executors.newFixedThreadPool(this.THREAD_POOL_COUNT, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread worker = new Thread(r);
worker.setName("TRACE-AGENT-WORKER");
worker.setDaemon(true);
return worker;
}
}); public static TraceAgent getTraceAgent() {
return traceAgent;
} public abstract void send(Span span); @PostConstruct
public void init() {
traceAgent = this;
}
}

10、配置类

FilterConfig.java

package com.dubbo.trace.configuration;

import com.dubbo.trace.restTemplate.RestTemplateFilter;
import com.dubbo.trace.servlet.TraceServletFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate; import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List; @Configuration
public class FilterConfig { /**
* httpClient客户端拦截器,需要clientRequestInterceptor,clientResponseInterceptor分别完成cs和cr操作
*
* @param brave
* @return
*/ @Autowired
private RestTemplate restTemplate; @Bean
RestTemplate template() {
return new RestTemplate();
} // 添加rest template拦截器
@PostConstruct
public void init() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>(restTemplate.getInterceptors());
interceptors.add(new RestTemplateFilter());
restTemplate.setInterceptors(interceptors);
} /**
* servlet过滤器,自定义过滤器完成cs和cr
*
* @return
*/
@Bean
public FilterRegistrationBean callTrackingServletFilter() {
TraceServletFilter filter = new TraceServletFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
List<String> urlPatterns = new ArrayList();
urlPatterns.add("/*");
registrationBean.setUrlPatterns(urlPatterns);
registrationBean.setOrder();
return registrationBean;
}
}

TraceConfig.java

package com.dubbo.trace.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; /**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月25日
* @since 1.0
*/
@ConfigurationProperties(prefix = "dubbo.trace")
@Component
public class TraceConfig { private boolean enabled;
private int connectTimeout;
private int readTimeout;
private int flushInterval;
private boolean compressionEnabled;
private String zipkinUrl;
private int serverPort = ;
private String applicationName; public boolean isEnabled() {
return enabled;
} public void setEnabled(boolean enabled) {
this.enabled = enabled;
} public int getConnectTimeout() {
return connectTimeout;
} public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
} public int getReadTimeout() {
return readTimeout;
} public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
} public int getFlushInterval() {
return flushInterval;
} public void setFlushInterval(int flushInterval) {
this.flushInterval = flushInterval;
} public boolean isCompressionEnabled() {
return compressionEnabled;
} public void setCompressionEnabled(boolean compressionEnabled) {
this.compressionEnabled = compressionEnabled;
} public String getZipkinUrl() {
return zipkinUrl;
} public void setZipkinUrl(String zipkinUrl) {
this.zipkinUrl = zipkinUrl;
} public int getServerPort() {
return serverPort;
} public void setServerPort(int serverPort) {
this.serverPort = serverPort;
} public String getApplicationName() {
return applicationName;
} public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
}

ZipkinConfig.java

package com.dubbo.trace.configuration;

import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.Brave.Builder;
import com.github.kristofa.brave.EmptySpanCollectorMetricsHandler;
import com.github.kristofa.brave.Sampler;
import com.github.kristofa.brave.SpanCollector;
import com.github.kristofa.brave.http.HttpSpanCollector;
import com.github.kristofa.brave.http.HttpSpanCollector.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class ZipkinConfig { @Autowired
TraceConfig traceConfig; /**
* 配置收集器
*
* @return
*/
@Bean("httpSpanCollector")
public HttpSpanCollector spanCollector() {
Config config = Config.builder().compressionEnabled(false).connectTimeout(traceConfig.getConnectTimeout())
.flushInterval(traceConfig.getFlushInterval()).readTimeout(traceConfig.getReadTimeout()).build();
return HttpSpanCollector.create(traceConfig.getZipkinUrl(), config, new EmptySpanCollectorMetricsHandler());
} /**
* Brave各工具类的封装
*
* @param spanCollector
* @return
*/
@Bean
public Brave brave(SpanCollector spanCollector) {
Builder builder = new Builder(traceConfig.getApplicationName());// 指定serviceName
builder.spanCollector(spanCollector);
builder.traceSampler(Sampler.create());// 采集率
return builder.build();
} }

11、启动配置

EnableTraceAutoConfiguration.java

package com.dubbo.trace.configuration;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; /**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月25日
* @since 1.0
*/
@Configuration
@ConditionalOnBean(annotation = EnableTraceAutoConfigurationProperties.class)
@AutoConfigureAfter(SpringBootConfiguration.class)
@EnableConfigurationProperties(TraceConfig.class)
@ComponentScan(basePackages = "com.dubbo.trace")
public class EnableTraceAutoConfiguration {
}

EnableTraceAutoConfigurationProperties.java

package com.dubbo.trace.configuration;

import java.lang.annotation.*;

/**
* @author 王柱星
* @version 1.0
* @title
* @time 2018年10月25日
* @since 1.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableTraceAutoConfigurationProperties {
}

12、自定义starter配置

resources/META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.dubbo.trace.configuration.EnableTraceAutoConfiguration

13、dubbo过滤器配置

resources/META-INF/com.alibaba.dubbo.rpc.Filter

traceConsumerFilter=com.dubbo.trace.dubbo.TraceConsumerFilter
traceProviderFilter=com.dubbo.trace.dubbo.TraceProviderFilter

五、验证

1、启动测试项目:

Springboot-Zipkin0、Springboot-Zipkin1

2、访问:http://192.168.1.100:8080/testDubbo

3、打开zipkin地址:

http://47.52.199.100:9411

六、代码地址

https://github.com/Star-Lordxing/trace_starter_xing

七、扩展思路

1、zipkin这一套目前是把数据存储在assandra内存中、对性能有较高要求可采用ES存储。zipkin本身已经提供该实现、修改启动参数即可。

2、zipkin中目前只能看到调用链简单信息,并不知道报错频率、平均调用时间、调用次数等、需要借助其他工具完成。

3、zipkin只能看到请求报错信息、没法看到业务报错信息,需要查看ELK。

调用链系列三、基于zipkin调用链封装starter实现springmvc、dubbo、restTemplate等实现全链路跟踪的更多相关文章

  1. Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  2. 扩展Python模块系列(三)----参数解析与结果封装

    在上一节中,通过一个简单的例子介绍了C语言扩展Python内建模块的整体流程,从本节开始讲开始深入讨论一些细节问题,在细节讨论中从始至终都会涉及[引用计数]的问题.首先讨论C语言封装的Python函数 ...

  3. 调用awk的三种方式

    调用awk的三种方式 调用awk有三种方式,一种为Shell命令行方式,另外两种是将awk程序写入脚本文件,然后执行该脚本文件.三种方式的命令格式归纳如下: 一.在Shell命令行输入命令调用awk, ...

  4. Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  5. Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  6. Keil MDK STM32系列(四) 基于抽象外设库HAL的STM32F401开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  7. Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  8. Keil MDK STM32系列(六) 基于抽象外设库HAL的ADC模数转换

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  9. 基于 Istio 的全链路灰度方案探索和实践

    作者|曾宇星(宇曾) 审核&校对:曾宇星(宇曾) 编辑&排版:雯燕 背景 微服务软件架构下,业务新功能上线前搭建完整的一套测试系统进行验证是相当费人费时的事,随着所拆分出微服务数量的不 ...

随机推荐

  1. 小trick总结

    一个圆上的整点数量不会很多.(Cf AIM TR 5 F) 二分图完美匹配求字典序最小的方案:先一遍匈牙利求出任意一组完美匹配.再跑一遍逐位确定,要求不能修改编号比它小的匹配.(LG 4100) 如果 ...

  2. Impacket官方使用指南

      什么是Impacket Impacket是用于处理网络协议的Python类的集合.Impacket专注于提供对数据包的简单编程访问,以及协议实现本身的某些协议(例如SMB1-3和MSRPC).数据 ...

  3. codeforces 793B - Igor and his way to work(dfs、bfs)

    题目链接:http://codeforces.com/problemset/problem/793/B 题目大意:告诉你起点和终点,要求你在只能转弯两次的情况下能不能到达终点.能就输出“YES”,不能 ...

  4. springboot的跨域

    https://www.cnblogs.com/520playboy/p/7306008.html 1.对于前后端分离的项目来说,如果前端项目与后端项目部署在两个不同的域下,那么势必会引起跨域问题的出 ...

  5. 20190311 Windows上ZooKeeper伪集群的实现

    1. 复制并修改conf/zoo.cfg文件 以zoo1.cfg为例: dataDir=E:\\Develop\\zookeeper\\3.4.6\\zookeeper-3.4.6\\data1 da ...

  6. PostgreSQL操作-psql基本命令

    一.建立数据库连接----------------接入PostgreSQL数据库: psql -h IP地址 -p 端口 -U 数据库名 之后会要求输入数据库密码 二.访问数据库 1.列举数据库:\l ...

  7. Object.keys()返回对象的属性

    <script> // 传入对象,返回属性名 let obj = { 'a': '123', 'b': '456' } console.log(Object.keys(obj)) //[& ...

  8. POJ - 1039 Pipe(计算几何)

    http://poj.org/problem?id=1039 题意 有一宽度为1的折线管道,上面顶点为(xi,yi),所对应的下面顶点为(xi,yi-1),假设管道都是不透明的,不反射的,光线从左边入 ...

  9. JavaScript基本操作之——九个 Console 命令

    一.显示信息的命令 console.log('hello'); console.info('信息'); console.error('错误'); console.warn('警告'); 二.占位符 c ...

  10. Jena搭建SPARQL查询RDF数据

    1 Jena搭建SPARQL查询RDF数据 1.1 Jena概要 · SPARQL是W3C的RDF数据工作组设计的一种查询语言和协议,用于RDF数据的查询.经过类似于JDK安装时候的配置,可以在命令行 ...