Dubbo + Zipkin + Brave 实现全链路追踪

       这两天看了好几篇帖子,写zipkin与dubbo整合的内容都不全面,忍不住亲自上手码一遍。

       利用zipkin可以对dubbo进行调用链监控,可以查到调用链中的dubbo服务的性能,并且dubbo提供了SPI的接口,能很容易完成并自定义相应的filter去监控dubbo服务。

ZipKin介绍 

Zipkin是一个致力于收集分布式服务的时间数据的分布式跟踪系统。

Zipkin 主要涉及四个组件:collector(数据采集),storage(数据存储),search(数据查询),UI(数据展示)。

github源码地址:https://github.com/openzipkin/zipkin。

Zipkin提供了可插拔数据存储方式:In-Memory,MySql, Cassandra, Elasticsearch

ZipKin部署与运行 (注意需要在linux下运行,JDK1.8)

1、下载zipkin

http://central.maven.org/maven2/io/zipkin/java/zipkin-server/2.11.7/zipkin-server-2.11.7-exec.jar

2、运行zipkin

(1)In-Memory方式 

1

nohup java -jar zipkin-server-2.11.7-exec.jar &

注意:内存存储,zipkin重启后数据会丢失,建议测试环境使用


(2)MySql方式 

目前只与MySQL的5.6-7。它的设计是易于理解,使用简单。但是,当数据量大时,查询很慢。性能不是很好。

创建数据库zipkin,建表 :

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

CREATE TABLE IF NOT EXISTS zipkin_spans (   

`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',   

`trace_id` BIGINT NOT NULL,   

`id` BIGINT NOT NULL,   

`nameVARCHAR(255) NOT NULL,   

`parent_id` BIGINT,   

`debug` BIT(1),   

`start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',   

`duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query'   

) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;   

ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate';   

ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';   

ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';   

ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';   

ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';   

CREATE TABLE IF NOT EXISTS zipkin_annotations (   

`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',   

`trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',   

`span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',   

`a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',   

`a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',   

`a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',   

`a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',   

`endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',   

`endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',   

`endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',   

`endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'   

) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;   

ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';   

ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';   

ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';   

ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';   

ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';   

ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';   

ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';   

CREATE TABLE IF NOT EXISTS zipkin_dependencies (   

`dayDATE NOT NULL,   

`parent` VARCHAR(255) NOT NULL,   

`child` VARCHAR(255) NOT NULL,   

`call_count` BIGINT   

) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;   

ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);

启动zipkin命令 

1

STORAGE_TYPE=mysql MYSQL_HOST=IP MYSQL_TCP_PORT=3306 MYSQL_DB=zipkin MYSQL_USER=username MYSQL_PASS=password nohup java -jar zipkin-server-2.11.7-exec.jar &

(3)Elasticsearch方式 

创建elasticsearch用户,安装启动Elasticsearch服务 
官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html 
zipkin启动命令

1

STORAGE_TYPE=elasticsearch ES_HOSTS=http://IP:9200 nohup java -jar zipkin-server-2.11.7-exec.jar &

启动成功访问地址:http://192.168.20.15:9411/zipkin/(192.168.20.15替换为对应zipkin部署服务器地址)

 效果图如下:

整合

    简单的描述一下,同ServletFilter一样,在dubbo中利用Filter过滤请求,传递TraceId等参数,生成相应的span传递给zipkin服务器。引入brave-instrumentation-dubbo-rpc包,这是一个SPI的Filter包。

1

2

3

4

5

6

7

8

9

10

11

12

13

<dependencies>  

      <dependency>  

        <groupId>io.zipkin.reporter2</groupId>  

        <artifactId>zipkin-sender-okhttp3</artifactId>  

        <version>2.7.10</version>  

      </dependency>  

   

      <dependency>  

             <groupId>io.zipkin.brave</groupId>  

             <artifactId>brave-instrumentation-dubbo-rpc</artifactId>  

             <version>5.4.3</version>  

       </dependency>  

</dependencies>

brave-instrumentation-dubbo-rpc包里面有一个TracingFilter的类,在其invoke方法中实现了对span的一些操作。

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

@Override  

public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {  

    if (tracer == nullreturn 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();  

      }  

    }  

  }

该Filter是以SPI的方式引入dubbo的,在默认文件/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter中,有这样的声明:

1

tracing=brave.dubbo.rpc.TracingFilter

然后在dubbo的配置中引入该Filter即可。

wholly-dubbo-provider.xml

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

<beans xmlns="http://www.springframework.org/schema/beans"  

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"  

       xmlns:context="http://www.springframework.org/schema/context"  

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  

    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd  

    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">  

   

    <!--指定Spring配置中用到的属性文件 -->  

    <bean id="propertyConfig"  

        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  

        <property name="locations">  

            <list>  

                <value>classpath:brave.properties</value>  

            </list>  

        </property>  

    </bean>  

       

    <bean id="zipkinProperties" class="com.whollyframework.dubbo.config.ZipkinProperties">  

        <property name="serviceName" value ="${brave.name}"/>  

        <property name="url" value ="${http.sender.address}"/>  

        <property name="connectTimeout" value ="${http.sender.connectTimeout}"/>  

        <property name="readTimeout" value ="${http.sender.readTimeout}"/>  

    </bean>  

    <!-- 提供方应用信息,用于计算依赖关系 -->  

    <dubbo:application name="demo-provider"/>  

   

    <!-- 使用multicast广播注册中心暴露服务地址 -->  

    <!-- <dubbo:registry address="multicast://224.5.6.7:1234"/> -->  

    <dubbo:registry address="zookeeper://localhost:2181"/>  

   

    <!-- 用dubbo协议在20880端口暴露服务 -->  

    <dubbo:protocol name="dubbo" port="20880"/>  

   

    <!-- 和本地bean一样实现服务 -->  

    <bean id="demoService" class="com.whollyframework.dubbo.provider.DemoServiceImpl"/>  

   

    <!-- 声明需要暴露的服务接口 -->  

    <dubbo:service interface="com.whollyframework.dubbo.api.DemoService" ref="demoService"/>  

   

    <context:component-scan base-package="com.whollyframework.dubbo" />  

    <dubbo:provider filter="tracing" />  

</beans>

wholly-dubbo-consumer.xml

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

<beans xmlns="http://www.springframework.org/schema/beans"  

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"  

       xmlns:context="http://www.springframework.org/schema/context"  

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  

    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd  

    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">  

   

    <!--指定Spring配置中用到的属性文件 -->  

    <bean id="propertyConfig"  

        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  

        <property name="locations">  

            <list>  

                <value>classpath:brave.properties</value>  

            </list>  

        </property>  

    </bean>  

       

    <bean id="zipkinProperties" class="com.whollyframework.dubbo.config.ZipkinProperties">  

        <property name="serviceName" value ="${brave.name}"/>  

        <property name="url" value ="${http.sender.address}"/>  

        <property name="connectTimeout" value ="${http.sender.connectTimeout}"/>  

        <property name="readTimeout" value ="${http.sender.readTimeout}"/>  

    </bean>  

       

    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->  

    <dubbo:application name="demo-consumer"/>  

   

    <!-- 使用multicast广播注册中心暴露发现服务地址 -->  

    <!-- <dubbo:registry address="multicast://224.5.6.7:1234"/> -->  

    <dubbo:registry address="zookeeper://localhost:2181"/>  

   

    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->  

    <dubbo:reference id="demoService" check="false" interface="com.whollyframework.dubbo.api.DemoService"/>  

       

    <context:component-scan base-package="com.whollyframework.dubbo" />  

    <dubbo:consumer filter="tracing" />  

</beans>

brave.properties

1

2

3

4

brave.name=provider  

http.sender.address=http://192.168.20.15:9411/api/v2/spans  

http.sender.connectTimeout=5000  

http.sender.readTimeout=10000

然后还需要为该Filter注入一个Tracing实例,并且该实例名必须为tracing。

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

@Configuration  

public class ZipkinConfiguration {  

   

    @Autowired  

    private ZipkinProperties properties;  

   

    @Bean  

    public Tracing tracing(){  

   

        Sender sender = OkHttpSender.create(properties.getUrl());  

   

        AsyncReporter reporter = AsyncReporter.builder(sender)  

                .closeTimeout(properties.getConnectTimeout(), TimeUnit.MILLISECONDS)  

                .messageTimeout(properties.getReadTimeout(), TimeUnit.MILLISECONDS)  

                .build();  

   

        Tracing tracing = Tracing.newBuilder()  

                .localServiceName(properties.getServiceName())  

                .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "shiliew"))  

                .sampler(Sampler.ALWAYS_SAMPLE)  

                .spanReporter(reporter)  

                .build();  

        return tracing;  

    }  

}

Tracing类的主要作用是针对span的操作做一些配置,并设置上传zipkin服务器。

效果

  在这里不演示如何配置dubbo服务,之前的博文有相关介绍。效果图如下:


示例下载:zipkin+dubbo整合示例

 

参考:

zipkin与dubbo整合-https://shiliewrain.github.io/2018/07/20/zipkin2-dubbo/

dubbo+zipkin调用链监控-https://www.cnblogs.com/ASPNET2008/p/6709900.html

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值