zipkin-brave提供对dubbo监控插件基于springboot(四)

基于dubbo提供zipkin链路跟踪

使用springboot来实现

这里我们可以先查看官方针对其他rpc的实现brave-grpc-3.9.0.jar

原理

针对dubbo调用前后进行拦截,创建span,关联parentSpanId,traceId

其中我们要实现4个接口

  • ClientRequestAdapter
  • ClientResponseAdapter
  • ServerRequestAdapter
  • ServerResponseAdapter

DubboClientRequestAdapter实现ClientRequestAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class DubboClientRequestAdapter implements ClientRequestAdapter {
private Map<String, String> headers;
private String spanName;
public DubboClientRequestAdapter(@Nullable Map<String, String> headers, @Nullable String spanName) {
this.headers = headers;
this.spanName = spanName;
}
@Override
public String getSpanName() {
return this.spanName;
}
@Override
public void addSpanIdToRequest(SpanId spanId) {
if (spanId == null) {
headers.put(DubboTraceConst.SAMPLED, "0");
} else {
headers.put(DubboTraceConst.SAMPLED, "1");
headers.put(DubboTraceConst.TRACE_ID, IdConversion.convertToString(spanId.traceId));
headers.put(DubboTraceConst.SPAN_ID, IdConversion.convertToString(spanId.spanId));
if (spanId.nullableParentId() != null) {
headers.put(DubboTraceConst.PARENT_SPAN_ID, IdConversion.convertToString(spanId.parentId));
}
}
}
@Override
public Collection<KeyValueAnnotation> requestAnnotations() {
return Collections.emptyList();
}
@Override
public Endpoint serverAddress() {
return null;
}
}

DubboClientResponseAdapter实现ClientResponseAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DubboClientResponseAdapter implements ClientResponseAdapter {
private StatusEnum status;
public DubboClientResponseAdapter(@Nullable StatusEnum status) {
this.status = status;
}
@Override
public Collection<KeyValueAnnotation> responseAnnotations() {
return Collections.singleton(KeyValueAnnotation.create(DubboTraceConst.STATUS_CODE, status.getDesc()));
}
}

DubboServerRequestAdapter实现ServerRequestAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class DubboServerRequestAdapter implements ServerRequestAdapter {
private Map<String, String> headers;
private String spanName;
public DubboServerRequestAdapter(@Nullable Map<String, String> headers, @Nullable String spanName) {
this.headers = headers;
this.spanName = spanName;
}
@Override
public TraceData getTraceData() {
final String sampled = headers.get(DubboTraceConst.SAMPLED);
if (sampled != null) {
if (sampled.equals("0") || sampled.toLowerCase().equals("false")) {
return TraceData.builder().sample(false).build();
} else {
final String parentSpanId = headers.get(DubboTraceConst.PARENT_SPAN_ID);
final String traceId = headers.get(DubboTraceConst.TRACE_ID);
final String spanId = headers.get(DubboTraceConst.SPAN_ID);
if (traceId != null && spanId != null) {
SpanId span = getSpanId(traceId, spanId, parentSpanId);
return TraceData.builder().sample(true).spanId(span).build();
}
}
}
return TraceData.builder().build();
}
@Override
public String getSpanName() {
return this.spanName;
}
@Override
public Collection<KeyValueAnnotation> requestAnnotations() {
return Collections.emptyList();
}
static SpanId getSpanId(String traceId, String spanId, String parentSpanId) {
return SpanId.builder().traceId(convertToLong(traceId)).spanId(convertToLong(spanId))
.parentId(parentSpanId == null ? null : convertToLong(parentSpanId)).build();
}
}

DubboServerResponseAdapter实现ServerResponseAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DubboServerResponseAdapter implements ServerResponseAdapter {
private StatusEnum status;
public DubboServerResponseAdapter(@Nullable StatusEnum status) {
this.status = status;
}
@Override
public Collection<KeyValueAnnotation> responseAnnotations() {
return Collections.singleton(KeyValueAnnotation.create(DubboTraceConst.STATUS_CODE, status.getDesc()));
}
}

dubbo调用拦截

dubbo的调用会执行filterChain,其中区分PROVIDER,CONSUMER 所以可以记录对应的四个时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
public class BraveDubboFilter implements Filter {
/**
* @tips:这里不要用注解的方式
*/
private ClientRequestInterceptor clientRequestInterceptor;
private ClientResponseInterceptor clientResponseInterceptor;
private ServerRequestInterceptor serverRequestInterceptor;
private ServerResponseInterceptor serverResponseInterceptor;
public void setClientRequestInterceptor(ClientRequestInterceptor clientRequestInterceptor) {
this.clientRequestInterceptor = clientRequestInterceptor;
}
public BraveDubboFilter setClientResponseInterceptor(ClientResponseInterceptor clientResponseInterceptor) {
this.clientResponseInterceptor = clientResponseInterceptor;
return this;
}
public BraveDubboFilter setServerRequestInterceptor(ServerRequestInterceptor serverRequestInterceptor) {
this.serverRequestInterceptor = serverRequestInterceptor;
return this;
}
public BraveDubboFilter setServerResponseInterceptor(ServerResponseInterceptor serverResponseInterceptor) {
this.serverResponseInterceptor = serverResponseInterceptor;
return this;
}
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
/*
* 监控的 dubbo 服务,不纳入跟踪范围
*/
if ("com.alibaba.dubbo.monitor.MonitorService".equals(invoker.getInterface().getName())) {
return invoker.invoke(invocation);
}
RpcContext context = RpcContext.getContext();
/*
* 调用的方法名 以此作为 span name
*/
String methodName = invocation.getMethodName();
/*
* provider 应用相关信息
*/
StatusEnum status = StatusEnum.OK;
if ("0".equals(invocation.getAttachment(DubboTraceConst.SAMPLED))
|| "false".equals(invocation.getAttachment(DubboTraceConst.SAMPLED))) {
return invoker.invoke(invocation);
}
//注入
if(!inject()) {
return invoker.invoke(invocation);
}
if (context.isConsumerSide()) {
System.out.println("consumer execute");
/*
* Client side
*/
clientRequestInterceptor.handle(new DubboClientRequestAdapter(invocation.getAttachments(), methodName));
Result result = null;
try {
result = invoker.invoke(invocation);
} catch (RpcException e) {
status = StatusEnum.ERROR;
throw e;
} finally {
final DubboClientResponseAdapter clientResponseAdapter = new DubboClientResponseAdapter(status);
clientResponseInterceptor.handle(clientResponseAdapter);
}
return result;
} else if (context.isProviderSide()) {
System.out.println("provider execute");
serverRequestInterceptor.handle(new DubboServerRequestAdapter(context.getAttachments(), methodName));
Result result = null;
try {
result = invoker.invoke(invocation);
} finally {
serverResponseInterceptor.handle(new DubboServerResponseAdapter(status));
}
return result;
}
return invoker.invoke(invocation);
}
private boolean inject() {
Brave brave = ApplicationContextHolder.getBean(Brave.class);
if(brave == null) {
return false;
}
this.setClientRequestInterceptor(brave.clientRequestInterceptor());
this.setClientResponseInterceptor(brave.clientResponseInterceptor());
this.setServerRequestInterceptor(brave.serverRequestInterceptor());
this.setServerResponseInterceptor(brave.serverResponseInterceptor());
return true;
}
}

使用springboot configuration

基于注解启用

1
2
3
4
5
6
7
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboTraceConfiguration.class)
public @interface EnableDubboTrace {
}

配置项

1
2
3
4
5
6
7
8
9
10
@Configuration
@ConditionalOnClass(Brave.class)
public class DubboTraceConfiguration {
@Bean
public ApplicationContextAware holder() {
return new ApplicationContextHolder();
}
}

ApplicationContextHolder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
setCtx(ctx);
}
private static void setCtx(ApplicationContext ctx) {
applicationContext = ctx;
}
public static <T> T getBean(Class<T> requiredType){
return applicationContext.getBean(requiredType);
}
public static Object getBean(String classStr) {
return applicationContext.getBean(classStr);
}
}

其他类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public interface DubboTraceConst {
String SAMPLED = "dubbo.trace.sampled";
String PARENT_SPAN_ID = "dubbo.trace.parentSpanId";
String SPAN_ID = "dubbo.trace.spanId";
String TRACE_ID = "dubbo.trace.traceId";
String STATUS_CODE = "dubbo.trace.staus_code";
}
public enum StatusEnum {
OK(200, "OK"),
ERROR(500, "ERROR");
private int code;
private String desc;
private StatusEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}

针对dubbo filter进行配置文件添加

1
2
src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
BraveDubboFilter=com.kite.zipkin.filter.BraveDubboFilter

如何使用

ps:前置条件是已经有了Brave

导入依赖

1
2
3
4
5
<dependency>
<groupId>com.kite.zipkin</groupId>
<artifactId>dubbo-zipkin-spring-starter</artifactId>
<version>1.0.0</version>
</dependency>

前置条件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Configuration
public class ZipkinConfig {
//span(一次请求信息或者一次链路调用)信息收集器
@Bean
public SpanCollector spanCollector() {
Config config = HttpSpanCollector.Config.builder()
.compressionEnabled(false)// 默认false,span在transport之前是否会被gzipped
.connectTimeout(5000)
.flushInterval(1)
.readTimeout(6000)
.build();
return HttpSpanCollector.create("http://localhost:9411", config, new EmptySpanCollectorMetricsHandler());
}
//作为各调用链路,只需要负责将指定格式的数据发送给zipkin
@Bean
public Brave brave(SpanCollector spanCollector){
Builder builder = new Builder("service1");//指定serviceName
builder.spanCollector(spanCollector);
builder.traceSampler(Sampler.create(1));//采集率
return builder.build();
}
}

启动dubboTrace

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDubboTrace
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

实现效果
image

ps