基于dubbo提供zipkin链路跟踪
使用springboot来实现
这里我们可以先查看官方针对其他rpc的实现brave-grpc-3.9.0.jar
原理
针对dubbo调用前后进行拦截,创建span,关联parentSpanId,traceId
其中我们要实现4个接口
- ClientRequestAdapter
- ClientResponseAdapter
- ServerRequestAdapter
- ServerResponseAdapter
DubboClientRequestAdapter实现ClientRequestAdapter
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
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
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
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 所以可以记录对应的四个时间
@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
基于注解启用
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboTraceConfiguration.class)
public @interface EnableDubboTrace {
}
配置项
@Configuration
@ConditionalOnClass(Brave.class)
public class DubboTraceConfiguration {
@Bean
public ApplicationContextAware holder() {
return new ApplicationContextHolder();
}
}
ApplicationContextHolder
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);
}
}
其他类
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进行配置文件添加
src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
BraveDubboFilter=com.kite.zipkin.filter.BraveDubboFilter
如何使用
ps:前置条件是已经有了Brave
导入依赖
<dependency>
<groupId>com.kite.zipkin</groupId>
<artifactId>dubbo-zipkin-spring-starter</artifactId>
<version>1.0.0</version>
</dependency>
前置条件配置
@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
@SpringBootApplication
@EnableDubboTrace
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
实现效果
ps
- 如果需要添加其他信息在requestAnnotations()里面进行添加
- 当前实现方式为依赖应用方提供Brave配置,如果不想由应用方提供可以使用springboot的,@ConditionalOnMissingBean来进行创建