上一篇中,我们介绍了使用 Spring Cloud Gateway 对服务入口进行统一管理。这一篇中,我们来介绍一下微服务的链路追踪。在一个大型的微服务系统当中,服务与服务之间的调用是错综复杂的,调用链路往往也是非常冗长的,当服务出现异常时,如果我们要一个个服务去查看日志定位异常的话,是相当麻烦的。为此,我们可以使用 SkyWalking 对微服务调用链路进行追踪,快速定位问题。同时,通过 SkyWalking,我们可以更清晰的观测分布式系统。

1. 关于 SkyWalking

SkyWalking 是一个现代化的应用程序性能监控(Application Performance Monitoring ,简称“APM”)系统。逻辑上分为四个部分:探测器、平台后端、存储和UI。以下是 SkyWalking 官网的架构图。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_链路追踪-SkyWalking

下面是用通俗的方式简化架构图。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_02

关于 SkyWalking 更多详细的介绍可以参考以下网址。

官网:https://skywalkinng.apache.org/

文档:https://skywalking.apache.org/docs/main/v9.6.0/readme

中文文档:https://skyapm.github.io/document-cn-translation-of-skywalking

2. 版本选择

虽然 SkyWalking 既不属于 Spring Cloud 体系,也不属于 Spring Cloud Alibaba 体系,但是

Java Agent 运行的 JDK 版本还是要考虑 Spring Cloud Alibaba 中 Spring Boot 的版本,因为 Java Agent 和 Spring Cloud Alibaba 中的微服务是运行在同一个 JDK 中的。由于我们的 Spring Cloud Alibaba 版本需要 Spring Boot 3.0.x 的版本,最低支持 JDK 17,因此 Java Agent 也要选择支持 JDK 17 的版本。当前官网提供的最新的几个下载版本为 8.14.0 - 9.0.0,支持 JDK 8 - 17,我们就选择 8.16.0 版本。至于 SkyWalking Servers(包含 oapService 和 webappService) 的版本,只需保证部署的版本与 Java Agent 之间服务的正常交互即可。

需要注意的是,SkyWalking Servers 当前官网提供的最新的几个下载版本为 9.0.0 - 9.6.0 版本,这些版本在 Linux 环境下均能正常启动和运行。但在 Windows 环境下, 9.4.0 - 9.6.0 版本一执行脚本,命令行窗口就闪退,闪退后连错误日志都不输出。 在这里,SkyWalking Servers 我们选择 9.2.0 的版本。

3. 安装

这里的安装,需要先安装 SkyWalking Servers ,然后再安装 Java Agent 。SkyWalking Servers 的安装包括 oapService 和 webappService 两部分;Java Agent 的安装,是在微服务 -jar 启动参数前,添加启动参数即可(例如:-javaagent:/path/to/skywalking-agent/skywalking-agent.jar)。

3.1. SkyWalking Servers

3.1.1. 文件下载

从官网下载 SkyWalking APM v9.2.0 版本(链接地址),下载 apache-skywalking-apm-9.2.0.tar.gz 包,解压后文件目录如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_03

3.1.2. 目录说明

下面对 apache-skywalking-apm-9.2.0.tar.gz 解压后的主要目录进行说明。

3.1.2.1. bin 目录

bin 目录中,其中 *.bat 文件是 Windows 环境下的操作脚本,*.sh 文件是在 Linux 环境下的操作脚本。oapService 是日志收集服务,webappService 是 UI 服务。如果 oapService 和 webappService 部署在同一台机器上,Windows 环境下执行 startup.bat 文件即可同时启动 oapService 和 webappService,Linux 环境下执行 startup.sh 文件即可同时启动 oapService 和 webappService。如果 oapService 和 webappService 不在同一台机器上,则需分别执行对应的文件进行启动。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud_04

3.1.2.2. config 目录

config 目录中,主要是一些关于 oapService 的配置文件,用得最多的还是 application.yml 文件,一般都会对他的存储方式进行修改,默认是使用 h2 进行存储,可以修改为 Mysql 或 ES 等。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_05

3.1.2.3. webapp 目录

webapp 目录中,存放的是 webappService(UI 界面-监控界面) 运行的 jar 包和配置文件。运行 skywalking-webapp.jar 包即可启动 UI 服务。在 application.yml 中,可以修改 UI 服务的端口及 oapService 的地址。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud_06

3.1.2.4. oap-libs 目录

oap-libs 目录中,是 oapService 运行的各种依赖包,其中 server-starter-9.6.0.jar 为 oapService 服务的启动包。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_07

3.1.3. 服务部署

3.1.3.1. oapService 配置修改

进入到 config 目录,修改 application.yml 的配置信息,将 selector: ${SW_STORAGE:h2} 改为 selector: ${SW_STORAGE:mysql},如下所示。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_08

同时配置 Mysql 连接信息(这里需要创建数据库 mall-skywalking,启动 oapService 会自动初始化数据库表),如下所示。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_09

由于我们的数据存储方式是 Mysql ,oap-libs 目录中无 Mysql 连接驱动包,我们需要下载一个驱动包放进去。直接到我们本地工程的 Maven 仓库找到 mysql-connector-j-8.0.32.jar 驱动包,放进 oap-libs 目录即可。

oapService 服务启动后,会暴漏两个端口,分别是 11800 端口和 12800 端口,其中 11800 端口是负责日志收集的端口,即与 Java Agent 之间交互的端口;12800 端口是负责提供日志查询的端口,即与 webappService(UI 服务)之间交互的端口。

3.1.3.2. webappService 配置修改

由于 webappService(UI 服务) 默认端口是 8080,容易与其他应用的端口造成冲突,我们将端口修改为 8086。进入到 webapp 目录,修改 application.yml 配置文件。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud_10

其中,8086 就我们访问 UI 的端口,uri: http://127.0.0.1:12800 就是 oapService 的服务地址。在这里,我们是把 oapService 和 webappService 都部署在同一台机器,因此 127.0.0.1 无需修改。

3.1.4. 启动服务

我们是把 oapService 和 webappService 部署在 Windows 环境下的同一台机器的,进入到 bin 目录,直接双击 startup.bat 即可。首次启动涉及到初始化数据库,可能启动相对比较久, SkyWalking APM v9.2.0 版本,有 334 张表。当启动情况出现下面信息,且 logs 文件夹下的日志无报错信息,即说明 oapService 和 webappService 已经成功启动了。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_11

此时可以看到,数据库表结构也被初始化了。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_12

浏览器输入 http://localhost:808/ 地址,即可访问。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_链路追踪-SkyWalking_13

3.2. Java Agent

3.2.1. 文件下载

从官网下载 Java Agent v8.16.0 版本(链接地址),下载 apache-skywalking-java-agent-8.16.0.tgz 包,解压后文件目录如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_14

3.2.2. 目录说明

Java Agent 下载解压后,我们主要是关注目录中的 skywalking-agent.jar 文件,因为启动微服务时,需要指定 skywalking-agent.jar 所在的路径,其他目录,都是 skywalking-agent.jar 文件运行时所依赖的一些包和插件。

3.2.3. 启动服务

Java Agent 是和我们的应用程序绑定一起启动的,可以参照官网的下列说明,对启动参数进行配置。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_15

以订单服务为例,如果是在本地开发,用 idea 进行启动,可以在 VM options 中,按如下方式进行配置。

-javaagent:D:\apache-skywalking-java-agent-8.16.0\skywalking-agent.jar
-DSW_AGENT_NAME=mall-order-service
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_链路追踪-SkyWalking_16

如果在 Linux 环境中,可以编写一个叫 mall-order-center-start.sh 的启动脚本, 如下所示。

#!/bin/sh
export SW_AGENT_NAME=mall-order-service
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
export JAVA_AGENT=-javaagent:/path/to/skywalking-package/agent/skywalking-agent.jar 
java $JAVA_AGENT -jar mall-order-center-start-1.0-SNAPSHOT.jar

其中,SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 是 oapService 的地址。

由于我们是通过网关进行访问的,需要将 optional-plugins 文件夹中的 apm-spring-cloud-gateway-3.x-plugin-8.16.0.jar、apm-spring-webflux-5.x-plugin-8.16.0.jar 两个 jar 包拷贝到 plugins 文件中。

启动网关、账户服务、商品服务、订单服务后,打开 SkyWalking 控制台,找到“普通服务” -> “服务”,可以看到已绑定 Java Agent 的服务列表。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_17

拓扑图如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_链路追踪-SkyWalking_18

4. 测试

我们在订单模块,调用”根据商品 id 查询商品信息“接口,输入不存在的商品id,如下所示。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud_19

idea 查看商品服务控制台报错日志如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_20

日志说 89 行代码中 productEntity 对象不能为空。此时,我们去 SkyWalking 控制台也查看一下日志。找到“普通服务” -> “服务”,再查看拓扑图如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_21

调用链路如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_22

逐个点击橙色的端点,当点击到第三个橙色的端点(即:GET:/client/product/v1/queryById/1)时,可以看到 “java.lang.IllegalArgumentException: Source must not be null”异常,该异常正是 idea 控制台日志输出的异常。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Boot3_23

5. 自定义链路追踪

到目前为止,我们在微服务工程中未添加一行代码,既已达到对微服务的基本监控。但是上面 “GET:/client/product/v1/queryById/1” 接口的异常信息,比起我们在 idea 看到的异常信息,少了一些重要的信息。例如,在上面的 idea 截图中,我们可以看到 ”org.example.product.app.executor.ProductExecutor.queryProductById(ProductExecutor.java:89)“ 的异常信息,这个信息非常重要,但是在上面“GET:/client/product/v1/queryById/1”接口的异常信息中未体现。为此,我们可以通过自定义链路追踪,来打印更多有用的追踪信息。

5.1. 异常信息打印

在商品模块的 app 层工程中添加如下 Maven 依赖。

<dependency>
  <groupId>org.apache.skywalking</groupId>
  <artifactId>apm-toolkit-trace</artifactId>
  <version>8.15.0<</version>
</dependency>

在 org.example.product.app.executor.ProductExecutor#queryProductById 方法上添加 @Trace 注解,如下所示。

@Trace
    public ProductRspDTO queryProductById(Long id) {
        ProductEntity productEntity = this.productService.getById(id);
        ProductRspDTO productRspDTO = new ProductRspDTO();
        BeanUtils.copyProperties(productEntity,productRspDTO);
        return productRspDTO;
    }

重启商品服务,再次调用”根据商品 id 查询商品信息“接口,链路信息如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Boot3_24

可以看到,链路中多了一个“
org.example.product.app.executor.ProductExecutor.queryProductById(java.lang.Long)”端点。点击进入查看该端点的日志信息,可以看到,已经打印出在 idea 中输出的那些重要信息了。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_25

5.2. 方法参数打印

通过 @Trace 与 @Tag 注解的配合使用,我们可以打印业务方法的的入参、出参,例如下面的代码。

@Trace
    @Tag(key = "inParam.id", value = "arg[0]")
    @Tag(key = "outParam.productCode", value = "returnedObj.productCode")
    @Tag(key = "outParam.count", value = "returnedObj.count")
    @Tag(key = "outParam.productName", value = "returnedObj.productName")
    @Tag(key = "outParam.price", value = "returnedObj.price")
    public ProductRspDTO queryProductById(Long id) {
        ProductEntity productEntity = this.productService.getById(id);
        ProductRspDTO productRspDTO = new ProductRspDTO();
        BeanUtils.copyProperties(productEntity,productRspDTO);
        return productRspDTO;
    }

Postman 对”根据商品 id 查询商品信息“接口,发送如下请求。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Boot3_26

再查看”org.example.product.app.executor.ProductExecutor.queryProductById(java.lang.Long)“ 端点日志信息。可以看到,已经打印出了我们通过 @Tag 注解指定的出参、入参。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud_27

6. 性能监控

找到 ”仪表盘“->”仪表盘“->”General-Service“。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_28

点击 ”General-Service“ 进去,给我们展示的是某个微服务整体的监控信息。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_29

我们可以切到 ”Trace Profiling“选项卡创建对某个接口的监控任务。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Boot3_30

例如对”根据商品 id 查询商品信息“接口做增加延时处理,如下所示。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_31

接口监控任务,配置如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_32

对接口发起请求后,监控信息如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud_33

此时,点击”分析“按钮,给我们展示了调用栈的分析结果。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_COLA_34

其中”java.lang.Thread.sleep:-2“特别显眼,执行了 2979ms。

7. 日志集成

到目前为止,我们的微服务是没有输出任何链路追踪日志的,例如,调用”根据商品 id 查询商品信息“接口,商品服务日志信息如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud_35

如果要输出链路追踪日志,我们需要将 SkyWalking 与日志框架进行集成。SkyWalking官方提供了 log4j、log4j2、logback 的三种集成方式。在 Spring Boot 中,默认的日志集成框架是 logback,那么下面我们就来对 logback 进行集成。

给商品模块 app 层工程添加如下 Maven 依赖。

<dependency>
  <groupId>org.apache.skywalking</groupId>
  <artifactId>apm-toolkit-logback-1.x</artifactId>
   <version>8.15.0<</version>
</dependency>

在商品服务 start 层工程 resource 目录下,新建 logback-spring.xml 文件,内容如下。

<configuration scan="true" scanPeriod=" 5 seconds">

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>

    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <discardingThreshold>0</discardingThreshold>
        <queueSize>1024</queueSize>
        <neverBlock>true</neverBlock>
        <appender-ref ref="STDOUT"/>
    </appender>
    <appender name="grpc-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="ASYNC"/>
        <appender-ref ref="grpc-log"/>
    </root>
</configuration>

重启商品服务服务,重新请求接口,控制台就可以看到 TID 了。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Boot3_36

将 TID 复制到 UI 控制台界面进行查询,效果如下。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Boot3_37

此时 Log 选项卡的日志信息仍旧为空,需要对 Jave Agent 的 agent.config 配置文件,添加相关的配置信息才会将日志信息传输到 oapService。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud Alibaba_38

给 agent.config 配置文件添加以下内容。

plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:127.0.0.1}
plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}

重启商品服务服务,重新请求接口,UI 控制台界面就可以看到日志信息了。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_链路追踪-SkyWalking_39

点击点击”追踪ID“ 即可跳转到追踪页面。

基于 COLA 架构的 Spring Cloud Alibaba(七) SkyWalking_Spring Cloud_40

8. 总结

本篇先分别介绍了 oapService(平台后端)、webappService(UI 服务)、 Java Agent(探测器)的安装和测试。接着介绍了在默认的基础上,如何增加打印业务方法的具体异常信息和方法参数。然后介绍了如何对接口进行性能监控和分析。最后介绍了如何集成日志框架和添加链路追踪id、追踪日志。通过异常信息的收集,性能的监控和分析,追踪日志的集成,我们可以更清晰的观测分布式系统。


基础篇项目代码:链接地址