5.SpringCloud:Skywalking链路追踪和应用监控

目录

 一、概述

二、Skywalking介绍

三、Skywalking服务端部署

四、Skywalking客户端

五、定制化Tag 

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

方式B(application-toolkit-trace  源代码方式)

方式C(customize-enhance-trace  插件方式)


 一、概述

  随着服务拆分,服务数量增加,调用链路也日趋复杂。因此需要专门工具完成链路追踪(Distributed Tracing)与应用监控(APM)。

    这种工具分为:采集端和服务端。

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

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

常见具体解决方案有: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可定位全链路中调用层级信息)

二、Skywalking介绍

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

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

SkyWalking 基本概念

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

官方Skywalking架构图如下:

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

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

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

三、Skywalking服务端部署

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

1、从官网找到下载页面

 2、从官网下载并解压

wget --no-check-certificate  https://dlcdn.apache.org/skywalking/8.8.1/apache-skywalking-apm-8.8.1.tar.gz
tar xvfz apache-skywalking-apm-8.8.1.tar.gz
cd apache-skywalking-apm-bin

 解压后目录结构为包括Skywalking后端和Web UI的目录:

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

 3、Skywalking后端默认使用数据库为H2。这里保持默认不变

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

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

 6、启动。在Linux中使用bin/startup.sh,会自动启动Skywalking后端即Web UI

 7、访问。http://39.100.80.168:38080/

四、Skywalking客户端

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

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

1、从官网找到下载页面

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

skywalking-agent
  ├─activations
  ├─bootstrap-plugins
  ├─config
  ├─licenses
  ├─logs
  ├─optional-plugins
  ├─optional-reporter-plugins
  └─plugins

  3、启动被探测的应用。这里已前文已经开发的payment、order服务为例分别启动,只需要增加-javaagent选项即可。

#启动payment支付服务
java -javaagent:D:\skywalking-agent\skywalking-agent.jar -DSW_AGENT_COLLECTOR_BACKEND_SERVICES='39.100.80.168:11800' -DSW_AGENT_NAME='payment' -jar payment-0.2.2-SNAPSHOT.jar

#启动order订单服务
java -javaagent:D:\skywalking-agent\skywalking-agent.jar -DSW_AGENT_COLLECTOR_BACKEND_SERVICES='39.100.80.168:11800' -DSW_AGENT_NAME='order' -jar order-1.0-SNAPSHOT.jar
  • 变量SW_AGENT_COLLECTOR_BACKEND_SERVICES:配置Skywalking后端服务的ip和port
  • 变量SW_AGENT_NAME:配置被采集的应用名,即采集后在Skywalking Web界面中展示的service name。

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

   4、至此。Skywalking服务端和客户端都部署完毕。通过curl发几笔业务,然后可以在Skywalking界面中看到如下。Trace界面中可以看到Skywalking默认自动采集的服务间调用追踪链路和耗时等信息。

在Topology拓扑界面中可以看到,自动生成的调用关系图:

五、定制化Tag 

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

    这些信息被称为TAG,即key=value对。(Skywalking默认自动采集的信息也是TAG形式)

 根据官网文档,可以分为两类实现机制:

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

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

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

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

    @Trace(operationName = "pay入口")  // operationName属性指定Span的名字,若不指定,会使用方法名
    @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总入口")  // operationName属性指定Span的名字,若不指定,会使用方法名
    @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);
    }

  4、为了在Skywalking服务端界面中可以用Tag搜索,需要在Skywalking服务端config/application.yml文件中,在searchableTracesTags配置的tag列表中增加以上自定义的Tag名,如下图。然后重启Skywalking服务端。

  5、然后重新package。重启payment和order服务。 

  6、通过curl发几笔业务,然后可以在Skywalking界面中看到如下。Trace界面中可以看到(且能通过Tag搜索):

   以上注解方式存在的缺点:通过@Trace注解虽然能达到效果,但在调用链路会多一层。经过测试:如果不加@Trace注解则无效。如果加上@Trace注解,则会如上图,又多一个Span Type=Local,层级被加深一层,很别扭。 

方式B(application-toolkit-trace  源代码方式)

  那么如果我们通过源代码能获取到当前的Span,并在当前Span追加Tag信息,不就能够解决问题了? 

1、在payment服务源代码中,删除@Trace@Tags@Tag注解,使用ActiveSpan获得当前的Span。代码如下:

    @GetMapping("/dopay/{orderid}")
    public ResponseEntity<String> paylogic(@PathVariable("orderid") Long orderid) {
        ActiveSpan.tag("orderid", orderid.toString());
        logger.info("Skywalking TraceID=[{}]", TraceContext.traceId());
        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);
    }

   2、同样order服务的源代码中,删除@Trace@Tags@Tag注解,使用ActiveSpan获得当前的Span。代码如下

    @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());
        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);
    }

  3、然后重新package。重启payment和order服务。 

  4、通过curl发几笔业务,然后可以在Skywalking界面中看到如下。自定义Tag已经带上,但调用层级信息并没有增加。

方式C(customize-enhance-trace  插件方式)

  以上两种方式都需要修改源代码,存在侵入性。 还可以通过插件+配置方式,达到完全的零侵入性。

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

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

 3、新建该插件配置文件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;

  4、(若有)去掉Java源代码中的上文Skywalking新增的代码并重新编译打包,恢复至原状。

  5、重启payment和order服务。

  6、通过curl发几笔业务,然后可以在Skywalking界面中看到如下。自定义Tag已经带上,但调用层级信息并没有增加。(测试了,在Skywalking界面没看到自定义Tag,效果没有出来。原因不明,待查.......)

猜你喜欢

转载自blog.csdn.net/zyplanke/article/details/121465355