springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]可观测性包括:​Metrics、Tracing、Logging

目录

一、概述

二、Skywalking介绍

三、内部传输协议

四、Skywalking服务端

五、Skywalking客户端

六、调整agent自带插件

七、定制化采集 

方式A(customize-enhance-trace插件+配置方式)

方式B(application-toolkit-trace注解方式)

方式C(application-toolkit-trace非注解方式)

情形C1:不构建新Span,使用已有Span

情形C2:构建新Span

方式D(opentracing源代码方式)


一、概述

      在传统的监控系统中,我们往往会关注虚拟机的 CPU、内存、网络、应用服务的接口请求量、资源使用率等指标,但在复杂系统中,这些监控指标并不足以帮助我们掌握系统的整体运行状况。在此背景下,系统的 “Observerbility可观测性” 应运而生。通常,我们认为可观测性相对于过去监控,最大的变化就是,系统需要处理的数据从指标为主,扩展到了更广的领域。目前对可观测性共识:可观测性包括三大支柱: ​   

  • Metrics
  • Tracing
  • Logging

  尤其是随着服务拆分,服务数量增加,调用链路也日趋复杂,对服务具备可观测性的要求越来越迫切。比如:分布式追踪(Distributed Tracing)能帮助我们直观地看到请求(request)或操作在各分布式系统里各个微服务的整个过程及其链路。

市面上已经有大量的开源项目,尽管不同工具定位、设计和实现不同,但通常都可以分为:采集端和服务端。

  • 采集端。在微服务应用一侧,用于采集微服务各个数据(包括调用链路)。
  • 服务端。接收采集端的数据并存放、加工和呈图。

采集端和服务端的传输方式有多种,比如通过日志埋点、接口通信等等。

常见具体解决方案有:Sleuth+Zipkin方案、Skywalking、CAT等。 

Sleuth+Zipkin作为Spring Cloud默认方案使用较多,但其监控UI呈图比较薄弱,不太符合中国风格。 因此这里选择使用Skywalking方案为例。无论哪个解决方案,在基础概念术语上多少都会采纳和借鉴Sleuth+Zipkin。比如:

  • Trace:即一次全链路调用。一次调用过程中涉及多个服务,一次全链路调用过程中多个服务的TraceID是相同的。(通过TraceID可定位一次全链路)
  • Span:服务间某各具体调用跨度,有点类似于网络通信的hop跳。这里的Span不是服务,而是服务间的调用。即Span不是下图的椭圆节点,而是调用线条。在同一个全链路TraceID中,每一次调用,SpanID就会增加1   (通过SpanID可定位全链路中调用次数信息)
  • Parent:光靠Trace+Span还不足以描述如下图节点B分别调用C和D的情况。因此还需要增加ParentID用于描述本次Span的上级调用者。即尽管先调用C后调用D(SpanID不同),但C和D都是被B调用,在调用层次上C和D属于同一层级(ParentID相同)。(通过ParentID可定位全链路中调用层级信息)

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

二、Skywalking介绍

SkyWalking 是一个开源可观察性平台,用于收集、分析、聚合和可视化来自服务和云原生基础设施的数据。SkyWalking 提供了一种简单的方法来维护分布式系统的清晰视图,即使是跨云也是如此。它是一种现代 APM,专为云原生、基于容器的分布式系统而设计。

 其官网为:https://skywalking.apache.org/

APM:应用性能管理(Application Performance Management)是一个比较新的网络管理方向,主要指对企业的关键业务应用进行监测、优化,提高企业应用的可靠性和质量。可简单理解监控体系。不同的监控侧重点不同,比如侧重硬件/CPU等资源的监控、侧重网络的监控。这里的Skywalking是侧重服务间链路调用的APM监控。

SkyWalking 基本概念

  • Service。表示一组/一组工作负载,它们为传入的请求提供相同的行为。您可以在使用仪器代理或 SDK 时定义服务名称。即微服务中的服务。
  • Service instance。服务组中的每个单独的工作负载称为一个实例。即同一个微服务可以部署多个节点,每个节点(进程)即为一个实例。
  • Endpoint。传入请求的服务路径,例如 HTTP URI 路径或 gRPC 服务类 + 方法签名。
  • Process。操作系统的进程。

官方Skywalking架构图如下:

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

  • Probes探针。采集数据并传输数据,部署在源,不同的探针以 支持不同的数据采集(见图中左侧)
  • Platform backend  后端平台。即数据聚合、加工、分析和流处理等 (见图中Receiver,也称为oap)
  • Storage 通过开放/可插拔接口存储SkyWalking 数据。您可以选择现有的实现,例如 H2、OpenSearch、ElasticSearch 6, 7, 8、MySQL、MySQL-Sharding(Shardingsphere-Proxy)、TiDB、PostgreSQL、BanyanDB,或者实现您自己的。 默认是H2,存在内存中,重启会丢失。(见图中Storage Options)
  • UI 健康呈图Web UI界面,webapp。高度可定制的基于 Web 的界面,允许 SkyWalking 最终用户可视化和管理 SkyWalking 数据。(见图中GUI/CLI)

Skywalking官方docs文档如下图,按不同的模块组织和排版。 

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 如果要查看老版文档,可以通过github阅读docs目录。如下:

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

三、内部传输协议

在SkyWalking内部传输Protocol为V3版本,也被称为sw8协议。该协议通过在Header中自动增加:

  • Header名:sw8
  • Header值:由-分隔的8个字段组成。Header值的长度应该小于2KB。

Header值中具体包含以下8个字段:

  1. 采样(Sample),0 或 1,0 表示上下文存在, 但是可以(也很可能)被忽略;1 表示这个追踪需要采样并发送到后端。
  2. 追踪ID(Trace Id),其容是由 . 分割的三个 long 类型值, 表示此追踪的唯一标识。BASE64 编码的字符串。
  3. 父追踪片段ID(Parent trace segment Id),内容是字符串且全局唯一。BASE64 编码的字符串。
  4. 父跨度ID(Parent span Id),是一个从 0 开始的整数,这个跨度ID指向父追踪片段(segment)中的父跨度(span)。
  5. 父服务名称(Parent service),内容是一个长度小于或等于50个UTF-8编码的字符串。BASE64 编码的字符串。
  6. 父服务实例标识(Parent service instance),内容是一个长度小于或等于50个UTF-8编码的字符串。BASE64 编码的字符串。
  7. 父服务的端点(Parent endpoint),内容是父追踪片段(segment)中第一个入口跨度(span)的操作名,由长度小于或等于50个UTF-8编码的字符组成。BASE64 编码的字符串。
  8. 本请求的目标地址(Peer),内容是客户端用于访问目标服务的地址(这个地址一般由代码人工或者插件等进行赋值)。通常为 IP:端口形式。BASE64 编码的字符串。  

另外,还可能存在sw8-correlationsw8-x 等Header名。 sw8-correlation的值内容是key:value形式的Correlation。BASE64 编码的字符串。

Correlation可跨Span间传递信息,实现跨服务调用的信息传递。与Tag不同,不会将Correlation报告给SkyWalking服务端。

常见开源APM项目使用的传输协议如下表

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

    注1:为了统一可观测性系统中的数据采集和标准规范,提供与供应商无关的接口,CNCF 把 OpenTracing 和 OpenCensus 合并后成为OpenTelemetry 项目。OpenTelemetry 通过 Spec 规范了观测数据的数据模型以及采集、处理、导出方法和标准,但不涉及数据如何去使用、存储、展示和告警等,由各产品进行具体实现。

    注2:OpenTelemetry 与 SkyWalking 存在一些共通的概念:都是使用 Trace 来定义一次追踪,并使用 Span 来标记追踪里的最小粒度。但是在一些细节和实现上还是会有差别

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

以上图片资料来源为:https://baijiahao.baidu.com/s?id=1754874021459504401

四、Skywalking服务端

服务端这里以CentOS作为服务器为例。服务端部署很简单,基本上执行解压和启动即可

1、从官网找到下载页面

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 2、从官网下载并解压。由于9.x版本的Web界面UI组织发生了较大变化,且9.4及更高版本需要运行在JDK11 to JDK17上。因此这里以8.9.1版为例。

wget --no-check-certificate https://archive.apache.org/dist/skywalking/8.9.1/apache-skywalking-apm-8.9.1.tar.gz
tar xvfz apache-skywalking-apm-8.9.1.tar.gz
cd apache-skywalking-apm-bin

#wget --no-check-certificate https://archive.apache.org/dist/skywalking/9.3.0/apache-skywalking-apm-9.3.0.tar.gz
#tar xvfz apache-skywalking-apm-9.3.0.tar.gz
#cd apache-skywalking-apm-bin

 解压后服务端包括:Skywalking后端Receiver(oap) 与 Web UI(webapp)的目录如下:

apache-skywalking-apm-bin
  ├── bin
  ├── config
  ├── config-examples
  ├── LICENSE
  ├── licenses
  ├── NOTICE
  ├── oap-libs
  ├── README.txt
  ├── tools
  └── webapp

 3、Skywalking支持存储种类包括数据库H2、OpenSearch、ElasticSearch 6, 7, 8、MySQL、MySQL-Sharding(Shardingsphere-Proxy)、TiDB、PostgreSQL、BanyanDB。默认使用数据库为H2(该数据为内存数据库,重启则数据全部丢失)。 仅做演示这里保持默认的H2不变

 4、Skywalking后端接收采集源数据的默认侦听:0.0.0.0/11800(gRPC API  包括接收 Java、DotNetCore、Node.js 和 Istio 代理/探针) 及  0.0.0.0/12800HTTP REST API)。若需要修改Skywalking接收采集数据的端口则修改config/application.yml文件。这里保持默认不变

 5、Skywalking的监控呈图Web界面端口默认为8080。若需要修改Skywalking接收采集数据的端口则修改webapp/webapp.yml文件。  由于本环境中8080端口已经被占用,因此改为38001端口

 6、启动。在Linux中使用bin/startup.sh,会自动启动Skywalking后端Receiver(oap)以及Web UI(webapp)。启动后会存在两个进程,两个进程监听端口如下:

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 7、访问Web。http://39.100.80.168:38001/

    注:点击时间旁边时区,可对界面显示语言等进行调整。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

五、Skywalking客户端

Skywalking客户端,即在微服务部署探针Probe作为数据的采集源。

对于Java类程序,Skywalking的探针是使用JVM Agent技术编写,可以做到对被探测的应用零侵入。

1、从官网找到下载页面

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

  

  2、下载后解压。解压后的agent目录结构如下:

skywalking-agent
  ├── activations
  ├── bootstrap-plugins
  ├── config
  ├── LICENSE
  ├── licenses
  ├── logs
  ├── NOTICE
  ├── optional-plugins
  ├── optional-reporter-plugins
  ├── plugins
  └── skywalking-agent.jar

  3、配置agent并启动被探测的应用。这里以前文已经开发的payment、order服务为例作为被探测应用。

    在解压后的agent目录下,编辑config/agent.config文件中collector.backend_service,将默认值最后的127.0.0.1:11800修改为Skywalking服务端的地址39.100.80.168:11800

    然后分别通过以下命令启动被探测的应用:

#启动payment支付服务
java -javaagent:D:/skywalking-agent/skywalking-agent.jar -DSW_AGENT_NAME=payment -DSW_AGENT_INSTANCE_NAME=inst11 -jar payment-0.2.2-SNAPSHOT.jar

#启动order订单服务
java -javaagent:D:/skywalking-agent/skywalking-agent.jar -DSW_AGENT_NAME=order -DSW_AGENT_INSTANCE_NAME=inst21 -jar order-1.0-SNAPSHOT.jar
  • -javaagent JVM参数以指定skywalking-agent.jar位置
  • 变量SW_AGENT_NAME:配置被采集的应用名,即采集后在Skywalking Web界面中展示的service name。
  • 变量SW_AGENT_INSTANCE_NAME:配置被采集的应用实例名,即采集后在Skywalking Web界面中展示的service name。若不指定会在启动时自动生成一串,为便于查看识别,建议指定。
  • 变量SW_AGENT_COLLECTOR_BACKEND_SERVICES:配置Skywalking后端服务的ip和port。由于服务端通常不变,建议通过agent.config配置文件中指定

除了在启动命令中使用-D选项配置方式外,也可在skywalking-agent/config/agent.config配置文件中配置

   4、至此。Skywalking服务端和客户端都部署完毕。通过curl调用应用发几笔业务

curl http://localhost:4444/consumer/77

  5、然后可以在Skywalking界面中看到如下。在Dashboard界面上的可查看实例信息。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 在Topology拓扑界面中可以看到,根据调用关系自动生成的拓扑关系图。红色为Unhealthy:

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

在Trace界面中可以查看采集的服务间调用追踪链路和耗时等信息(注意按持续时间,还是开始时间排序)

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 点击上图右侧链路各Span,可以看到米格Span的详细信息,包括自带的Tags值:springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

Span Type在OpenTracing的概念为Span Kind。 具体见:https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/java-plugin-development-guide/#span  

六、调整agent自带插件

   agent采用插件模式。

  • plugins目录下的插件会在启动时会自动被加载并生效;
  • optional-plugins目录下的插件不会加载,可存放暂不生效的插件。

  因为由对应的插件采集,才有上图采集后的信息。在上图中使用插件包括SpringMVC和feign。如果我们把agent的插件 *springmvc*  和 *feign*plugins目录下移动至optional-plugins目录。 再次重启,会发现未自动采集到对应的数据。

   因此,可通过调整插件(存放在不同目录),开启关闭不同类别的采集项,实现按需采集

 agent源代码:https://github.com/apache/skywalking-java/

七、定制化采集 

   Skywalking默认采集的信息不包括应用系统的业务信息,比如业务流水号、客户号等有助于识别和定位的关键信息。因此需要采集更多信息提供给Skywalking。

    这些信息被称为TAG,即key=value对。(Skywalking各插件默认自动采集的信息也是TAG形式)。 定制Tags可通过多种方式实现:

  • customize-enhance-trace 。通过插件+配置方式,完全不修改源代码。可参考:https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/customize-enhance-trace/
  • application-toolkit-trace注解方式。 需要对源代码修改,增加注解。可参考:https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/application-toolkit-trace/
  • application-toolkit-trace非注解方式。 需要对源代码修改,增加代码,更加灵活。参考同上。
  • opentracing。 需要对源代码修改,增加代码(不推荐)。可参考:https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/opentracing/

方式A(customize-enhance-trace插件+配置方式)

  可以通过插件+配置方式,达到完全的零侵入性,适合不用或无法修改源代码的情形。

1、在客户端skywalking-agent目录下,将插件apm-customize-enhance-plugin-8.16.0.jar和apm-spring-annotation-plugin-8.16.0.jar,从目录optional-plugins/拷贝至plugin/目录,使之启用生效。

2、新建该插件配置文件customize_enhance_trace.xml(路径保持与前步骤一致,字符编码要与XML头行声明的一致)。内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<enhanced>
    <class class_name="com.example.payment.contronller.PaymentController">
        <method method="paylogic(java.lang.Lang)" operation_name="pay调用入口" static="false">
            <tag key="orderid">arg[0]</tag>
			<tag key="test">测试PPP</tag>
        </method>
    </class>
    <class class_name="com.example.order.contronller.OrderController">
        <method method="consumerFeign(java.lang.Lang)" operation_name="consumer调用入口" static="false">
            <tag key="orderid">arg[0]</tag>
			<tag key="test">测试CCC</tag>
        </method>
    </class>
</enhanced>

XML内容更多配置定义,请参考:https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/customize-enhance-trace/

特别需要注意的是method的写法:

  • 基本类型: 基本类型.class ,例如: int.class
  • 非基本类型: 类的完全限定名称 ,例如:java.lang.String
  • 数组:可以写个数组print一下,就知道格式了。例如:[Ljava.lang.String;

3、指定该插件的配置文件。在skywalking-agent目录下,在config/agent.config文件中新增plugin.customize.enhance_file属性,指向该插件配置文件的绝对路径。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 4、(若已增加,可忽略本步)为了在Skywalking服务端界面中可以用Tag搜索,需要在Skywalking服务端config/application.yml文件中,在该文件中的core/default下的searchableTracesTags配置的tag列表中增加以上自定义的Tag名,如下图。修改后重启Skywalking服务端。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

5、重启payment和order服务(参照上文,启动命令需带上javaagent和以SW开头的参数)。

6、通过curl发几笔业务。然后可以在Skywalking界面中看到:自定义Tag已经带上。

方式B(application-toolkit-trace注解方式)

 1、在被采集的应用中增加依赖。pom.xml如下:

<!-- 前面省略 -->
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-trace</artifactId>
            <version>8.16.0</version>
        </dependency>
<!-- 后面省略 -->

  2、在payment服务源代码中,使用@Trace开启,并通过@Tags@Tag注解自定义需要采集的信息。如下

    @Trace(operationName = "pay入口")  // 此注解创建的Span Type=Local。operationName指定该Span名(在Skywalking界面展示为Endpoint),若不指定则使用方法名
    @Tags({@Tag(key = "orderid", value = "arg[0]"), @Tag(key = "return", value = "returnedObj")})
    @GetMapping("/dopay/{orderid}")
    public ResponseEntity<String> paylogic(@PathVariable("orderid") Long orderid) {
        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        int insert_rows = jdbcTemplate.update("insert into T_PAY (order_id, amount) values (" + orderid +", 999)");
        logger.info("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
        return ResponseEntity.ok("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
    }

3、同样order服务的源代码中,使用@Trace开启,并通过@Tags@Tag注解自定义需要采集的信息。如下

    @Trace(operationName = "consumer总入口")  // 此注解创建的Span Type=Local。operationName指定该Span名(在Skywalking界面展示为Endpoint),若不指定则使用方法名
    @Tags({@Tag(key = "orderid", value = "arg[0]"), @Tag(key = "return", value = "returnedObj")})
    @GetMapping("/consumer/{orderid}")
    @GlobalTransactional(timeoutMills = 28000, name = "gts-seata-example")
    public ResponseEntity<String> consumerFeign(@PathVariable("orderid") Long orderid) {
        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        String result = paymentServiceClient.dodopay(orderid).getBody(); // 调用支付服务进行支付
        logger.info("[paymentService result]: {}", result);

        jdbcTemplate.update("insert T_Order(order_id, address, remark) values (" + orderid + ", 'dizhi', '"+result+"')");

        //模拟在支付完成后,本订单事务后续处理过程中发生异常后,全局回滚功能
        if (forcefail) {
            throw new RuntimeException("模拟在支付完成后,本订单事务后续处理过程中发生异常!");
        }

        return ResponseEntity.ok ("调用订单服务完成。 订单orderid:" + orderid + ",   调用支付返回的Body:" + result);
    }

注意: 必须先有注解@Trace,注解@Tags@Tag才生效。如果没有注解@Trace,而只有@Tags@Tag则不生效。

  4、(若已增加,可忽略本步)为了在Skywalking服务端界面中可以用Tag搜索,需要在Skywalking服务端config/application.yml文件中,在该文件中的core/default下的searchableTracesTags配置的tag列表中增加以上自定义的Tag名,如下图。修改后重启Skywalking服务端。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

  5、然后对模块通过maven重新package打包。重启payment和order服务(参照上文,启动命令需带上javaagent和以SW开头的参数)。 

  6、通过curl发几笔业务。然后可以在Skywalking界面中看到如下。Trace界面中可以看到(且能通过Tag搜索):springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 点开每个Span,能看到其每个Tag具体信息:

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

   以上通过@Trace注解虽然能达到效果,但在调用链路显示会多一层。

   这是因为既存在经@Trace开启而增加的Span,也存在agent自带*feign*插件采集的Span内容,两者结果叠加在一起,所以多一层Span Type=Local,层级被加深一层,图上看着比较别扭。 办法:既可以通过调整agent加载的插件来减少不不要的层次,也可通过下面application-toolkit-trace方式。

方式C(application-toolkit-trace非注解方式)

  

情形C1:不构建新Span,使用已有Span

不构建新Span,而是使用已有Span(例如:由agent自带*feign*插件采集的Span),再已有Span础上获取相关信息及增加Tag等。那么如果我们通过源代码能获取到当前Trace的Span,并在当前Span追加Tag信息,而不是新增Span,即可避免上面多增加一层的不足。

 1、在被采集的应用中增加依赖(若已存在则无需再次增加)。pom.xml如下:

<!-- 前面省略 -->
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-trace</artifactId>
            <version>8.16.0</version>
        </dependency>
<!-- 后面省略 -->

 2、在payment服务源代码中,删除@Trace@Tags@Tag注解,通过ActiveSpan获得当前的Span并增加tag;通过TraceContext获取当前traceID(不可修改traceID)并获取Correlation。代码如下:

    @GetMapping("/dopay/{orderid}")
    public ResponseEntity<String> paylogic(@PathVariable("orderid") Long orderid) {

        ActiveSpan.tag("orderid", orderid.toString());
        logger.info("Skywalking TraceID=[{}]", TraceContext.traceId());
        logger.info("CorrSeq={}", TraceContext.getCorrelation("CorrSeq"));

        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        int insert_rows = jdbcTemplate.update("insert into T_PAY (order_id, amount) values (" + orderid +", 999)");
        logger.info("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
        return ResponseEntity.ok("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
    }

Correlation可跨Span间传递信息,实现跨服务调用的信息传递。与Tag不同,不会将Correlation报告给SkyWalking服务端。

 3、同样order服务的源代码中,删除@Trace@Tags@Tag注解,通过ActiveSpan获得当前的Span并增加tag;通过TraceContext获取当前traceID(不可修改traceID)并放置Correlation()。代码如下:

    @GetMapping("/consumer/{orderid}")
    @GlobalTransactional(timeoutMills = 28000, name = "gts-seata-example")
    public ResponseEntity<String> consumerFeign(@PathVariable("orderid") Long orderid) {

        ActiveSpan.tag("orderid", orderid.toString());
        logger.info("Skywalking TraceID=[{}]", TraceContext.traceId());
        TraceContext.putCorrelation("CorrSeq", "ID12345600000000000");

        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        String result = paymentServiceClient.dodopay(orderid).getBody(); // 调用支付服务进行支付
        logger.info("[paymentService result]: {}", result);

        jdbcTemplate.update("insert T_Order(order_id, address, remark) values (" + orderid + ", 'dizhi', '"+result+"')");

        //模拟在支付完成后,本订单事务后续处理过程中发生异常后,全局回滚功能
        if (forcefail) {
            throw new RuntimeException("模拟在支付完成后,本订单事务后续处理过程中发生异常!");
        }

        return ResponseEntity.ok ("调用订单服务完成。 订单orderid:" + orderid + ",   调用支付返回的Body:" + result);
    }

 4、(若已增加,可忽略本步)为了在Skywalking服务端界面中可以用Tag搜索,需要在Skywalking服务端config/application.yml文件中,在该文件中的core/default下的searchableTracesTags配置的tag列表中增加以上自定义的Tag名,如上图。修改后重启Skywalking服务端。

 5、然后对模块通过maven重新package打包。重启payment和order服务(参照上文,启动命令需带上javaagent和以SW开头的参数)。 

 6、通过curl发几笔业务。然后可以在Skywalking界面中看到如下。自定义Tag已经带上,但图中调用层级信息并没有增加。 另:查看应用服务日志文件可看到log输出的TraceID及Correlation信息。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 特别说明①:可以通过ActiveSpan.error()或ActiveSpan.error(String errorMsg)将一个Span结果置为错误(非成功),以及错误信息内容。

 特别说明②:注解方式与非注解方式两者可混用,但如此混用代码会比较乱,不推荐。

情形C2:构建新Span

先将*feign*插件从plugins/目录中移除,不使用agent自带插件采集的Span。然后通过Tracer.createXXX()构建新Span,这样可以自行选择Span类型、设置Peer、注入/提取ContextCarrierRef信息、更加控制Span范围(例如仅将部分处理过程纳入Span)等,更加灵活可控。可参考:

https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/application-toolkit-tracer/

 1、在被采集的应用中增加依赖(若已存在则无需再次增加)。pom.xml如下:

<!-- 前面省略 -->
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-trace</artifactId>
            <version>8.16.0</version>
        </dependency>
<!-- 后面省略 -->

 2、在payment服务源代码中,删除@Trace@Tags@Tag注解,通过构建新Span并开始(结合stopSpan一起可控制Span范围),ActiveSpan获得当前的Span并增加tag;通过TraceContext获取当前traceID(不可修改traceID)并获取Correlation。代码如下:

    @GetMapping("/dopay/{orderid}")
    public ResponseEntity<String> paylogic(@PathVariable("orderid") Long orderid) {
        // operationName指定该Span名,在Skywalking界面展示为Endpoint
        Tracer.createEntrySpan("pay入口", null);
        ActiveSpan.tag("orderid", orderid.toString());
        logger.info("Skywalking TraceID=[{}]", TraceContext.traceId());
        logger.info("CorrSeq={}", TraceContext.getCorrelation("CorrSeq"));

        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        int insert_rows = jdbcTemplate.update("insert into T_PAY (order_id, amount) values (" + orderid +", 999)");
        logger.info("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
        Tracer.stopSpan(); //结束当前Span
        return ResponseEntity.ok("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
    }

Correlation可跨Span间传递信息,实现跨服务调用的信息传递。与Tag不同,不会将Correlation报告给SkyWalking服务端。

 ContextCarrierRef是承载当前Span上下文的对象。 

  • 可通过:Tracer.inject(contextCarrierRef)将Span当前上下文信息并注入到contextCarrierRef对象中(然后再从该Ref对象获取信息,例如遍历操作)。
  • Tracer.extract(contextCarrierRef)将contextCarrierRef对象中提取KeyValue数据并放置到Span上下文。

 具体示例代码可参考:

https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/application-toolkit-tracer/#injectextract-context-carrier

  3、同样order服务的源代码中,删除@Trace@Tags@Tag注解,通过构建新Span并开始(结合stopSpan一起可控制Span范围),ActiveSpan获得当前的Span并增加tag;通过TraceContext获取当前traceID(不可修改traceID)并放置Correlation。代码如下:

    @GetMapping("/consumer/{orderid}")
    @GlobalTransactional(timeoutMills = 28000, name = "gts-seata-example")
    public ResponseEntity<String> consumerFeign(@PathVariable("orderid") Long orderid) {

        // operationName指定该Span名,在Skywalking界面展示为Endpoint。对于Exit类型须指定Peer
        Tracer.createExitSpan("consumer总入口", "1.1.1.1:12345");
        ActiveSpan.tag("orderid", orderid.toString());
        logger.info("Skywalking TraceID=[{}]", TraceContext.traceId());
        TraceContext.putCorrelation("CorrSeq", "ID12345600000000000");

        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        String result = paymentServiceClient.dodopay(orderid).getBody(); // 调用支付服务进行支付
        logger.info("[paymentService result]: {}", result);

        jdbcTemplate.update("insert T_Order(order_id, address, remark) values (" + orderid + ", 'dizhi', '"+result+"')");

        //模拟在支付完成后,本订单事务后续处理过程中发生异常后,全局回滚功能
        if (forcefail) {
            throw new RuntimeException("模拟在支付完成后,本订单事务后续处理过程中发生异常!");
        }
        Tracer.stopSpan(); //结束当前Span
        return ResponseEntity.ok ("调用订单服务完成。 订单orderid:" + orderid + ",   调用支付返回的Body:" + result);
    }

 4、(若已增加,可忽略本步)为了在Skywalking服务端界面中可以用Tag搜索,需要在Skywalking服务端config/application.yml文件中,在该文件中的core/default下的searchableTracesTags配置的tag列表中增加以上自定义的Tag名,如上图。修改后重启Skywalking服务端。

 5、然后对模块通过maven重新package打包。重启payment和order服务(参照上文,启动命令需带上javaagent和以SW开头的参数)。 

 6、通过curl发几笔业务。然后可以在Skywalking界面中看到如下。自定义Tag已经带上,但图中调用层级信息并没有增加。 另:查看应用服务日志文件可看到log输出的TraceID及Correlation信息。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 

 特别说明①:可以通过ActiveSpan.error()或ActiveSpan.error(String errorMsg)将一个Span结果置为错误(非成功),以及错误信息内容。

 特别说明②:注解方式与非注解方式两者可混用,但如此混用代码会比较乱,不推荐。

方式D(opentracing源代码方式)

现在OpenTracing 和 OpenCensus 合并后成为OpenTelemetry 项目。

使用application-toolkit-trace方式,可以增加Tag、携带Correlation。除此之外,还可以通过opentracing源代码开发方式实(官方已经将其标为Deprecated)。

    Skywalking作为Opentracing实现产品之一,两者能支持对接,因此可以使用APIOpenTracing进行开发。不过Skywalking官方文档介绍非常简单(如下图),可查阅OpenTracing官网进一步了解:https://opentracing.io/

   

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 1、在被采集的应用中先去除apm-toolkit-trace依赖,然后增加apm-toolkit-opentracing依赖。pom.xml如下:

<!-- 前面省略 -->
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-opentracing</artifactId>
            <version>8.16.0</version>
        </dependency>
<!-- 后面省略 -->

 2、在payment服务源代码中,构建新Span并通过开始start-结束deactivate来控制范围。代码如下:

    @GetMapping("/dopay/{orderid}")
    public ResponseEntity<String> paylogic(@PathVariable("orderid") Long orderid) {

        Tracer tracer = new SkywalkingTracer();
        ActiveSpan activeSpan = tracer.buildSpan("pay入口")  // operationName指定该Span名,在Skywalking界面展示为Endpoint
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER) // 在Skywalking界面展示为Span Type=Entry。若不设置则默认为Local
                .startActive(); // 开始Span
        activeSpan.setTag("orderid", orderid.toString());   // 增加自定义tag

        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        int insert_rows = jdbcTemplate.update("insert into T_PAY (order_id, amount) values (" + orderid +", 999)");
        logger.info("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);

        try {  Thread.sleep(2000); } catch (InterruptedException e) { ; } // 模拟逻辑处理耗时2秒
        activeSpan.deactivate(); // 结束Span

        return ResponseEntity.ok("支付服务successful! orderid=" + orderid + ", 支付成功。 支付服务的端口为port=" + myport);
    }

注意:以上代码中SkywalkingTracer、Tracer、ActiveSpan、Tags是io.opentracing的,与前文的toolkit-trace的源代码对象是不同的。

 3、同样order服务的源代码中,构建新Span并通过开始start-结束deactivate来控制范围。代码如下

    @GetMapping("/consumer/{orderid}")
    @GlobalTransactional(timeoutMills = 28000, name = "gts-seata-example")
    public ResponseEntity<String> consumerFeign(@PathVariable("orderid") Long orderid) {

        Tracer tracer = new SkywalkingTracer();
        ActiveSpan activeSpan = tracer.buildSpan("consumer总入口")  // operationName指定该Span名,在Skywalking界面展示为Endpoint
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT) // 在Skywalking界面展示为Span Type=Exit。若不设置则默认为Local
                .withTag(Tags.PEER_HOST_IPV4.getKey(), "1.1.1.1:12345") // 对于Client类型(既Exit类型)必须指定Peer,否则SPAN_KIND指定无效仍为默认Local。在Skywalking界面展示为Peer,Peer也会纳入Topology拓扑图中
                .startActive(); // 开始Span
        activeSpan.setTag("orderid", orderid.toString());  // 增加自定义tag

        try {  Thread.sleep(500); } catch (InterruptedException e) { ; } // 模拟逻辑处理耗时0.5秒
        activeSpan.deactivate(); // 结束Span


        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        String result = paymentServiceClient.dodopay(orderid).getBody(); // 调用支付服务进行支付
        logger.info("[paymentService result]: {}", result);

        jdbcTemplate.update("insert T_Order(order_id, address, remark) values (" + orderid + ", 'dizhi', '"+result+"')");

        //模拟在支付完成后,本订单事务后续处理过程中发生异常后,全局回滚功能
        if (forcefail) {
            throw new RuntimeException("模拟在支付完成后,本订单事务后续处理过程中发生异常!");
        }

        return ResponseEntity.ok ("调用订单服务完成。 订单orderid:" + orderid + ",   调用支付返回的Body:" + result);
    }

 4、(若已增加,可忽略本步)为了在Skywalking服务端界面中可以用Tag搜索,需要在Skywalking服务端config/application.yml文件中,在该文件中的core/default下的searchableTracesTags配置的tag列表中增加以上自定义的Tag名,如上图。修改后重启Skywalking服务端。

 5、然后对模块通过maven重新package打包。重启payment和order服务(参照上文,启动命令需带上javaagent和以SW开头的参数)。 

 6、通过curl发几笔业务。然后可以在Skywalking界面中看到如下。可以看到几个Span阶段的耗时分别为0.5秒和2秒,还可以查看各Span的Tag。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 7、但是,观察发现调用链路图存在的分叉,与实际真实的链路不相符。原因分析如下:

在Opentracing中,Span与Span的关系有两种:

  • ChildOf 关系。 一笔HTTP调用,被调用的服务端的Span与发起调用的客户端Span,两者就构成了 ChildOf 关系(服务端的Span 是 客户端Span 的Child)。 这种ChildOf 关系中,父Span 都要等待子Span结果,子Span耗时影响了其所在父Span 耗时,在链路图上的耗时时间轴图上,父Span 耗时包含了所有的子Span耗时。  通过ChildOf关系可反映调用的层级深度
  • FollowsFrom关系。同一级调用内,先后的不同调用。在链路图上的耗时时间轴图上,处于FollowsFrom关系的Span是并列关系。通过FollowsFrom关系可反映同层调用的次数顺序

以上概念及图示参见:https://opentracing.io/docs/best-practices/instrumenting-your-application/

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

而通过查看Tracer源代码注释可发现,可对一个Span隐含或显示指定其所属的父Span。前面代码在调用startActive()时,会隐含自动取当前已有的Span作为其父Span。

然而,order服务的源代码中,构建新Span并通过开始start-结束deactivate控制的范围,没有将“dopay发出调用动作”包含在其中,所以“consumer总入口”与“dopay发出调用动作”两个Span没有形成ChildOf关系,而成为了FollowsFrom关系。

因此,修改源代码,将结束deactivate后移,使得“dopay发出调用动作”纳入到开始start-结束deactivate范围内。修改后的order服务的源代码如下(另payment服务也可以按此思路修改,略)

    @GetMapping("/consumer/{orderid}")
    @GlobalTransactional(timeoutMills = 28000, name = "gts-seata-example")
    public ResponseEntity<String> consumerFeign(@PathVariable("orderid") Long orderid) {

        Tracer tracer = new SkywalkingTracer();
        ActiveSpan activeSpan = tracer.buildSpan("consumer总入口")  // operationName指定该Span名,在Skywalking界面展示为Endpoint
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT) // 在Skywalking界面展示为Span Type=Exit。若不设置则默认为Local
                .withTag(Tags.PEER_HOST_IPV4.getKey(), "1.1.1.1:12345") // 对于Client类型(既Exit类型)必须指定Peer,否则SPAN_KIND指定无效仍为默认Local。在Skywalking界面展示为Peer,Peer也会纳入Topology拓扑图中
                .startActive(); // 开始Span
        activeSpan.setTag("orderid", orderid.toString());  // 增加自定义tag

        logger.info("全局事务XID=[{}], 事务BranchType=[{}] ", RootContext.getXID(), RootContext.getBranchType());
        String result = paymentServiceClient.dodopay(orderid).getBody(); // 调用支付服务进行支付
        logger.info("[paymentService result]: {}", result);

        jdbcTemplate.update("insert T_Order(order_id, address, remark) values (" + orderid + ", 'dizhi', '"+result+"')");

        //模拟在支付完成后,本订单事务后续处理过程中发生异常后,全局回滚功能
        if (forcefail) {
            throw new RuntimeException("模拟在支付完成后,本订单事务后续处理过程中发生异常!");
        }

        try {  Thread.sleep(500); } catch (InterruptedException e) { ; } // 模拟逻辑处理耗时0.5秒
        activeSpan.deactivate(); // 结束Span

        return ResponseEntity.ok ("调用订单服务完成。 订单orderid:" + orderid + ",   调用支付返回的Body:" + result);
    }

 8、然后对模块通过maven重新package打包。重启payment和order服务(参照上文,启动命令需带上javaagent和以SW开头的参数)。 

 9、通过curl发几笔业务。然后可以在Skywalking界面中看到如下。调用链路图已与实际情况相符,耗时时间轴也符合预期。

同时,与上个链路图对比还可观察到,下图少了由插件自动采集的Feign调出“/dopay”。原因是:由插件自动采集的Feign调出“/dopay”的Span Type=Exit,而以上源代码中“consumer总入口”的Span Type指定为Exit,两者均是Exit,违背了调用链路中两直接Span应该是Exit与Entry对应(既在父子两Span关系中,应当先有父Span的Entry,然后才能是子Span的Exit)。若存在连续两个Exit的Span,则最先的Span生效(本例中,以上源代码中“consumer总入口”先于Feign调出“/dopay”)。

如果把以上源代码中“consumer总入口”的Span Type指定为Entry,则与插件自动采集的调入“GET:/consumer”两者都是Entry,违背了调用链路中两直接Span应该是Exit与Entry对应(既在父子两Span关系中,应当先有父Span的Entry,然后才能是子Span的Exit)。若存在连续两个Entry的Span,则最后的Span生效。(本例中,调入“GET:/consumer”先于以上源代码中“consumer总入口”)。

 因此,如把以上源代码中“consumer总入口”指定Span Type指定为Local,则不会影响插件自动采集。

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

 特别说明①:可以通过以下代码将一个Span结果置为错误(非成功),以及错误信息内容。

        activeSpan.setTag(Tags.ERROR.getKey(), true);   // 设置Span结果为失败
        activeSpan.log("模拟失败信息");

  特别说明②:OpenTracing标准中本身可通过Baggage方式完成跨Span间传递信息,实现跨服务调用的信息传递(类似Skywalking中传递Correlation)。但,Skywalking在对OpenTracing支持实现过程中并未提供此功能,具体见apm-toolkit-opentracing源代码及注释:

springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]

今天的文章springcloud sleuth链路追踪_rocketmq消息链路追踪[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/89450.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注