dubbo + zipkin 实现全链路追踪
随着业务的发展,应用的规模不断的扩大,传统的应用架构无法满足诉求,服务化架构改造势在必行,以 Dubbo 为代表的分布式服务框架成为了服务化改造架构中的基石。随着微服务理念逐渐被大众接受,应用进一步向更细粒度拆分,并且,不同的应用由不同的开发团队独立负责,整个分布式系统变得十分复杂。没有人能够清晰及时的知道当前系统整体的依赖关系。当出现问题时,也无法及时知道具体是链路上的哪个环节出了问题。
本文介绍使用 dubbo zipkin 来实现全链路追踪,便于清晰的看出项目中各服务的调用关系以及各链路的信息。
zipkin的介绍、安装请参考http://dubbo.apache.org/zh-cn/blog/use-zipkin-in-dubbo.html 这篇文章介绍了zipkin 以及dubbo与zipkin的集成。
我主要来分享一下我在集成过程中遇到的问题:
报错信息如下
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are: PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'filter' threw exception; nested exception is java.lang.IllegalStateException: No such extension tracing for filter/org.apache.dubbo.rpc.Filter at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:121) at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:75) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1566) ... 13 more
这里主要是因为 我在注册dubbo服务时添加的filter没有找到所导致的错误
经过分析:注册服务时添加的filter是从 com.alibaba.dubbo.rpc.Filter 文件中 tracing = brave.dubbo.rpc.TracingFilter 调用的TracingFilter 过滤器而不是我spring配置 文件中配置的bean。如下图
我们配的filter=“tracing”实际调用的是下面文件中的过滤器
下面我们看一下brave.dubbo.rpc.TracingFilter中的代码:
package brave.dubbo.rpc; import brave.Span; import brave.Span.Kind; import brave.Tracer; import brave.Tracing; import brave.internal.Platform; import brave.propagation.Propagation; import brave.propagation.TraceContext; import brave.propagation.TraceContextOrSamplingFlags; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.extension.Activate; import com.alibaba.dubbo.common.extension.ExtensionLoader; import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory; import com.alibaba.dubbo.remoting.exchange.ResponseCallback; import com.alibaba.dubbo.rpc.Filter; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.Result; import com.alibaba.dubbo.rpc.RpcContext; import com.alibaba.dubbo.rpc.RpcException; import com.alibaba.dubbo.rpc.protocol.dubbo.FutureAdapter; import com.alibaba.dubbo.rpc.support.RpcUtils; import java.net.InetSocketAddress; import java.util.Map; import java.util.concurrent.Future; @Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, value = "tracing") // http://dubbo.io/books/dubbo-dev-book-en/impls/filter.html // public constructor permitted to allow dubbo to instantiate this public final class TracingFilter implements Filter { Tracer tracer; TraceContext.Extractor<Map<String, String>> extractor; TraceContext.Injector<Map<String, String>> injector; /** * {@link ExtensionLoader} supplies the tracing implementation which must be named "tracing". For * example, if using the {@link SpringExtensionFactory}, only a bean named "tracing" will be * injected. */ public void setTracing(Tracing tracing) { tracer = tracing.tracer(); extractor = tracing.propagation().extractor(GETTER); injector = tracing.propagation().injector(SETTER); } @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { if (tracer == null) return invoker.invoke(invocation); RpcContext rpcContext = RpcContext.getContext(); Kind kind = rpcContext.isProviderSide() ? Kind.SERVER : Kind.CLIENT; final Span span; if (kind.equals(Kind.CLIENT)) { span = tracer.nextSpan(); injector.inject(span.context(), invocation.getAttachments()); } else { TraceContextOrSamplingFlags extracted = extractor.extract(invocation.getAttachments()); span = extracted.context() != null ? tracer.joinSpan(extracted.context()) : tracer.nextSpan(extracted); } if (!span.isNoop()) { span.kind(kind); String service = invoker.getInterface().getSimpleName(); String method = RpcUtils.getMethodName(invocation); span.name(service + "/" + method); parseRemoteAddress(rpcContext, span); span.start(); } boolean isOneway = false, deferFinish = false; try (Tracer.SpanInScope scope = tracer.withSpanInScope(span)) { Result result = invoker.invoke(invocation); if (result.hasException()) { onError(result.getException(), span); } isOneway = RpcUtils.isOneway(invoker.getUrl(), invocation); Future<Object> future = rpcContext.getFuture(); // the case on async client invocation if (future instanceof FutureAdapter) { deferFinish = true; ((FutureAdapter) future).getFuture().setCallback(new FinishSpanCallback(span)); } return result; } catch (Error | RuntimeException e) { onError(e, span); throw e; } finally { if (isOneway) { span.flush(); } else if (!deferFinish) { span.finish(); } } } static void parseRemoteAddress(RpcContext rpcContext, Span span) { InetSocketAddress remoteAddress = rpcContext.getRemoteAddress(); if (remoteAddress == null) return; span.remoteIpAndPort(Platform.get().getHostString(remoteAddress), remoteAddress.getPort()); } static void onError(Throwable error, Span span) { span.error(error); if (error instanceof RpcException) { span.tag("dubbo.error_code", Integer.toString(((RpcException) error).getCode())); } } static final Propagation.Getter<Map<String, String>, String> GETTER = new Propagation.Getter<Map<String, String>, String>() { @Override public String get(Map<String, String> carrier, String key) { return carrier.get(key); } @Override public String toString() { return "Map::get"; } }; static final Propagation.Setter<Map<String, String>, String> SETTER = new Propagation.Setter<Map<String, String>, String>() { @Override public void put(Map<String, String> carrier, String key, String value) { carrier.put(key, value); } @Override public String toString() { return "Map::set"; } }; static final class FinishSpanCallback implements ResponseCallback { final Span span; FinishSpanCallback(Span span) { this.span = span; } @Override public void done(Object response) { span.finish(); } @Override public void caught(Throwable exception) { onError(exception, span); span.finish(); } } }
我们可以发现类上面的这个注解 @Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, value = "tracing")
注入的值是tracing 这时调用的才是我们在配置文件中的bean。
形象图如下:
导包历史如下:
调整完事启动服务完美运行: 依赖关系展示成功