zipkin链路跟踪

作者: 索伦x | 来源:发表于2019-05-23 20:56 被阅读41次

    Zipkin是什么


    Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开发贡献。其主要功能是聚集来自各个异构系统的实时监控数据。分布式跟踪系统还有其他比较成熟的实现,例如:Naver的Pinpoint、Apache的HTrace、阿里的鹰眼Tracing、京东的Hydra、新浪的Watchman,美团点评的CAT,skywalking等。

    为什么使用 Zipkin


    随着业务越来越复杂,系统也随之进行各种拆分,特别是随着微服务架构和容器技术的兴起,看似简单的一个应用,后台可能有几十个甚至几百个服务在支撑;一个前端的请求可能需要多次的服务调用最后才能完成;当请求变慢或者不可用时,我们无法得知是哪个后台服务引起的,这时就需要解决如何快速定位服务故障点,Zipkin分布式跟踪系统就能很好的解决这样的问题。

    Zipkin 架构


    zipkin架构

    如图所示,Zipkin 主要由四部分构成:收集器、数据存储、查询以及 Web 界面。Zipkin 的收集器负责将各系统报告过来的追踪数据进行接收;而数据存储默认使用 Cassandra,也可以替换为 MySQL;查询服务用来向其他服务提供数据查询的能力,而 Web 服务是官方默认提供的一个图形用户界面。

    而各个异构的系统服务向 Zipkin 报告数据的架构如下图。


    跟踪信息采集

    Zipkin 客户端 Brave


    Brave 是用来装备 Java 程序的类库,提供了面向 Standard Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等接口的装备能力,可以通过编写简单的配置和代码,让基于这些框架构建的应用可以向 Zipkin 报告数据。同时 Brave 也提供了非常简单且标准化的接口,在以上封装无法满足要求的时候可以方便扩展与定制。

    如下图是Brave的结构图。Brave利用reporter向zipkin的Collector发送trace信息。


    Brave

    Zipkin安装

    docker-compose.yml

    version: '2'
    
    services:
      # The zipkin process services the UI, and also exposes a POST endpoint that
      # instrumentation can send trace data to. Scribe is disabled by default.
      zipkin:
        image: openzipkin/zipkin
        container_name: zipkin
        environment:
          #- STORAGE_TYPE=mem
          - STORAGE_TYPE=mysql
          # Point the zipkin at the storage backend
          - MYSQL_DB=zipkin
          - MYSQL_USER=root
          - MYSQL_PASS=123456
          - MYSQL_HOST=192.168.1.8
          - MYSQL_TCP_PORT=3306
          # Uncomment to enable scribe
          # - SCRIBE_ENABLED=true
          # Uncomment to enable self-tracing
          # - SELF_TRACING_ENABLED=true
          # Uncomment to enable debug logging
          # - JAVA_OPTS=-Dlogging.level.zipkin=DEBUG -Dlogging.level.zipkin2=DEBUG
        ports:
          # Port used for the Zipkin UI and HTTP Api
          - 9411:9411
          # Uncomment if you set SCRIBE_ENABLED=true
          # - 9410:9410
        #networks: 
        #  - default 
        #  - my_net #创建网路 docker network create my_net 删除网络 docker network rm my_net
    #networks: 
      #my_net: 
        #external: true
    

    访问测试: http://localhost:9411

    Dubbo 集成 Zipkin

      1. POM.xml
        <properties>
            <brave.version>4.19.2</brave.version>
            <zipkin-reporter.version>2.1.3</zipkin-reporter.version>
            <zipkin.version>2.8.1</zipkin.version>
        </properties>
        <dependencies>
            <!-- 非WEB启动器 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <!-- dubbo启动器 -->
            <dependency>
                <groupId>com.alibaba.boot</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>0.2.0</version>
            </dependency>
    
            <dependency>
                <groupId>io.zipkin.reporter2</groupId>
                <artifactId>zipkin-reporter</artifactId>
                <version>2.6.0</version>
                <type>pom</type>
            </dependency>
            <dependency>
                <groupId>io.zipkin.reporter2</groupId>
                <artifactId>zipkin-sender-okhttp3</artifactId>
                <version>2.6.0</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave</artifactId>
                <version>4.19.2</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave-context-log4j2</artifactId>
                <version>4.19.2</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave-instrumentation-http</artifactId>
                <version>4.19.2</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave-instrumentation-http-tests</artifactId>
                <version>4.19.2</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave-instrumentation-servlet</artifactId>
                <version>4.19.2</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.brave</groupId>
                <artifactId>brave-tests</artifactId>
                <version>4.19.2</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.zipkin2</groupId>
                <artifactId>zipkin</artifactId>
                <version>${zipkin.version}</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.java</groupId>
                <artifactId>zipkin</artifactId>
                <version>${zipkin.version}</version>
            </dependency>
            <dependency>
                <groupId>io.zipkin.java</groupId>
                <artifactId>zipkin-junit</artifactId>
                <version>${zipkin.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.13</version>
            </dependency>
        </dependencies>
    
      1. JAVA配置类
    package com.suoron.login.api.config;
    
    import brave.Tracing;
    import brave.servlet.TracingFilter;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import zipkin2.reporter.AsyncReporter;
    import zipkin2.reporter.Reporter;
    import zipkin2.reporter.Sender;
    import zipkin2.reporter.okhttp3.OkHttpSender;
    import zipkin2.Span;
    import javax.servlet.Filter;
    
    @Configuration
    public class TracingConfig {
    
        /**
         * 配置zipkin服务地址
         */
        @Value("${zipkin.tracing.endpoint:http://10.3.135.166:9411/api/v2/spans}")
        private String zipkinEndPoint;
    
        @Value("${zipkin.tracing.local-service-name:local-service-name}")
        private String localServiceName;
    
        /**
         * 配置sender
         * @return
         */
        @Bean
        public Sender sender(){
            OkHttpSender sender = OkHttpSender
                    .newBuilder()
                    .endpoint(zipkinEndPoint)
                    .build();
            return sender;
        }
    
        /**
         * 配置reporter
         * @param sender
         * @return
         */
        @Bean
        public Reporter<Span> reporter(Sender sender){
            return AsyncReporter
                    .builder(sender)
                    .build();
        }
    
        /**
         * 配置dubbo-consumer tracing
         * @param reporter
         * @return
         */
        @Bean
        public Tracing tracing(Reporter reporter){
            return Tracing.newBuilder()
                    .localServiceName(localServiceName)
                    .spanReporter(reporter)
                    .build();
        }
    
        /**
         * 配置http tracing
         * @param reporter
         * @return
         */
        @Bean
        public Tracing tracing2(Reporter reporter){
            return Tracing.newBuilder()
                    .localServiceName(localServiceName + "_http")
                    .spanReporter(reporter)
                    .build();
        }
    
        /**
         * 配置servlet filter
         * @param tracing2
         * @return
         */
        @Bean
        public Filter filter(Tracing tracing2){
            return TracingFilter.create(tracing2);
        }
    
        /**
         * 注册filter
         * @param filter
         * @return
         */
        @Bean
        public FilterRegistrationBean filterRegistration(Filter filter) {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(filter);
            registration.addUrlPatterns("/*");
            registration.setName("zipkin-filter");
            registration.setOrder(1);
            return registration;
        }
    }
    
      1. Dubbo过滤器
        DubboZipkinConsumerFilter.java
    package com.suoron.login.api.filter;
    
    import brave.Span;
    import brave.Tracer;
    import brave.Tracing;
    import brave.propagation.TraceContext;
    import com.alibaba.dubbo.common.Constants;
    import com.alibaba.dubbo.common.extension.Activate;
    import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory;
    import com.alibaba.dubbo.rpc.*;
    import com.alibaba.dubbo.rpc.support.RpcUtils;
    import com.suoron.login.api.utils.ZipkinHelper;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.Map;
    
    import static com.suoron.login.api.utils.ZipkinHelper.SETTER;
    
    @Activate(group = Constants.CONSUMER)
    public class DubboZipkinConsumerFilter implements Filter {
        private static final Logger log = LoggerFactory.getLogger(DubboZipkinConsumerFilter.class);
    
        private SpringExtensionFactory springExtensionFactory = new SpringExtensionFactory();
        private Tracer tracer;
    
        // tracing上下文消息注入
        private TraceContext.Injector<Map<String, String>> injector;
    
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            log.info("dubbo zipkin consumer filter......");
    
            Tracing tracing = springExtensionFactory.getExtension(Tracing.class, "tracing");
            tracer = tracing.tracer();
            if (tracer == null){
                return invoker.invoke(invocation);
            }
    
            if (null == injector){
                injector = tracing.propagation().injector(SETTER);
            }
    
            RpcContext rpcContext = RpcContext.getContext();
            Span span = tracer.nextSpan();
            injector.inject(span.context(), invocation.getAttachments());
    
            ZipkinHelper.buildSpan(span, Span.Kind.CONSUMER, rpcContext.getRemoteAddress(), invoker.getInterface().getSimpleName(),
                    RpcUtils.getMethodName(invocation));
    
            return ZipkinHelper.spanTracing(span, tracer, invoker, invocation, rpcContext);
        }
    
    }
    

    DubboZipkinProviderFilter.java

    package com.suoron.login.api.filter;
    
    import brave.Span;
    import brave.Tracer;
    import brave.Tracing;
    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.config.spring.extension.SpringExtensionFactory;
    import com.alibaba.dubbo.rpc.*;
    import com.alibaba.dubbo.rpc.support.RpcUtils;
    import com.suoron.login.api.utils.ZipkinHelper;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.Map;
    
    import static com.suoron.login.api.utils.ZipkinHelper.*;
    
    @Activate(group = Constants.PROVIDER)
    public class DubboZipkinProviderFilter implements Filter {
    
        private static final Logger log = LoggerFactory.getLogger(DubboZipkinProviderFilter.class);
    
        private SpringExtensionFactory springExtensionFactory = new SpringExtensionFactory();
        private Tracer tracer;
    
        // tracing上下文消息提取
        private TraceContext.Extractor<Map<String, String>> extractor;
    
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            log.info("dubbo zipkin provider filter......");
    
            Tracing tracing = springExtensionFactory.getExtension(Tracing.class, "tracing");
            tracer = tracing.tracer();
            if (null == tracer){
                return invoker.invoke(invocation);
            }
    
            if (null == extractor){
                extractor = tracing.propagation().extractor(GETTER);
            }
    
            TraceContextOrSamplingFlags extracted = extractor.extract(invocation.getAttachments());
            Span span = extracted.context() != null
                    ? tracer.joinSpan(extracted.context())
                    : tracer.nextSpan(extracted);
    
            RpcContext rpcContext = RpcContext.getContext();
            ZipkinHelper.buildSpan(span, Span.Kind.SERVER, rpcContext.getRemoteAddress(), invoker.getInterface().getSimpleName(),
                    RpcUtils.getMethodName(invocation));
    
            return ZipkinHelper.spanTracing(span, tracer, invoker, invocation, rpcContext);
        }
    }
    

    ZipkinHelper.java -- 辅助类

    package com.suoron.login.api.utils;
    
    import brave.Span;
    import brave.Tracer;
    import brave.propagation.Propagation;
    import com.alibaba.dubbo.remoting.exchange.ResponseCallback;
    import com.alibaba.dubbo.rpc.*;
    import com.alibaba.dubbo.rpc.protocol.dubbo.FutureAdapter;
    import com.alibaba.dubbo.rpc.support.RpcUtils;
    import com.alibaba.fastjson.JSON;
    import zipkin2.Endpoint;
    
    import java.net.InetSocketAddress;
    import java.util.Map;
    import java.util.concurrent.Future;
    
    /**
     * @author Suoron
     * @Description:
     * @date 2018/6/22 10:44
     */
    public class ZipkinHelper {
    
        public 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 JSON.toJSONString(this);
                    }
                };
        public 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 JSON.toJSONString(this);
                    }
                };
    
        public static void buildSpan(Span span, Span.Kind kind, InetSocketAddress remoteAddress, String service, String method){
            if (!span.isNoop()) {
                span.kind(kind).start();
                span.kind(kind);
                span.name(service + "/" + method);
                Endpoint.Builder remoteEndpoint = Endpoint.newBuilder().port(remoteAddress.getPort());
                if (!remoteEndpoint.parseIp(remoteAddress.getAddress())) {
                    remoteEndpoint.parseIp(remoteAddress.getHostName());
                }
                span.remoteEndpoint(remoteEndpoint.build());
            }
        }
    
        public static Result spanTracing(Span span, Tracer tracer, Invoker<?> invoker, Invocation invocation, RpcContext rpcContext){
            boolean isOneway = false;
            boolean 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 ZipkinHelper.FinishSpanCallback(span));
                }
                return result;
            } catch (Exception e) {
                onError(e, span);
                throw e;
            } finally {
                if (isOneway) {
                    span.flush();
                } else if (!deferFinish) {
                    span.finish();
                }
            }
        }
    
        public static void onError(Throwable error, Span span) {
            span.error(error);
            if (error instanceof RpcException) {
                span.tag("dubbo.error_code", Integer.toString(((RpcException) error).getCode()));
            }
        }
    
        public 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();
            }
        }
    }
    
      1. 配置文件
        META-INF/spring.factories
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.suoron.login.api.config.TracingConfig
    

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

    zipkinConsumerDubboFilter=com.suoron.login.api.filter.DubboZipkinConsumerFilter
    zipkinProviderDubboFilter=com.suoron.login.api.filter.DubboZipkinProviderFilter
    

    application.yml

    # 消费端配置
    zipkin:
      tracing:
        local-service-name: login-consumer
    # 提供者配置
    zipkin:
      tracing:
        local-service-name: login-provider
    
      1. 过滤器使用
    // 提供者接口实现类
    @Service(version = "1.0.0",token = "12345678",filter = "zipkinProviderDubboFilter")
    public class ILoginServiceImpl implements ILoginService {
        ...
    }
    // 消费者调用类
        @Reference(version = "1.0.0",filter = "zipkinConsumerDubboFilter")
        public ILoginService iLoginService;
    

    相关文章

      网友评论

        本文标题:zipkin链路跟踪

        本文链接:https://www.haomeiwen.com/subject/jkcmzqtx.html