SpringCloud 分布式追踪

[toc]

一、基础知识

1.1 为什么要使用分布式追踪

微服务在架构、敏捷开发、快速部署、去中心化、组件化等方面带来便捷和优势。但是随着微服务框架落地,分布式架构和微服务架构给系统性能分析和问题定位带来非常大挑战。因此能够汇聚业务系统各处理环节的实时数据,实现对应用的全链路性能监测的微服务监控工具必不可少。

基于微服务体系之下构建的业务系统存在的问题基本上分为三类:

  • 故障定位难,一个简单操作,其背后可能是由多个微服务共同完成的,这些微服务也由不同的团队去负责。一旦出现问题,最坏情况下我们也许需要这十几个团队一起来解决问题。
  • 链路梳理难,应用没有形成应用拓扑,不知道自己的服务下游会影响其他哪些人。
  • 资源浪费多,容量预估难。对于一些服务,其消耗的cpm和memory可能连10%不到,远远没有充分利用物理机。这其实和容量预估关联,过大或者过小估算峰值的机器容量,都是浪费。

APM主要的目的就是解决上面所说的问题,主要的手段是通过收集、存储、分析、分布式系统中的调用事件数据,协助开发运营人员进行故障诊断、容量预估、性能瓶颈定位以及调用链路梳理。

市面上APM产品众多,选择合适的APM产品主要从以下需求进行对比选型:

  • 代码的侵入性
  • 探针的性能消耗
  • 全面的调用链路数据分析
  • 可扩展性

本文选择skywalking作为微服务APM框架,skywalking相当于其他APM产品在性能消耗方面更有优势,且代码零入侵、可扩展性强并且其支持动态配置可以很好的和微服务框架进行集成。


1.2 分布式追踪实现原理

1.2.1 术语

  • Trace 一次分布式调用的链路
  • Span 一次本地或者远程方法的调用
  • Annotation 附加在 Span 上的日志信息
  • Sampling 采样率(客户端按照比例将埋点信息提交给服务端

1.2.2 原理

分布式追踪理论主要包括以下两个关键点:

  • 为了实现分布式追踪,当请求发送到分布式系统的入口端点时,分布式服务追踪框架为该请求创建一个唯一的追踪标识,同时在分布式系统内部流转时,追踪框架始终保持传递该唯一标识,直到返回给请求方为止,这个唯一标识就是Trace ID,而一次完整的调用链路就是Trace。通过Trace ID我们就能将请求链路进行关联起来。
  • 为了统计各单位的时间延迟,当我们请求达到各个服务组件、或者处理逻辑到达某个状态时,分布式服务追踪框架创建一个唯一标识来标记它的开始、具体过程以及结束,该标识就是Span。

1.3 OpenTracing?

1.3.1 OpenTracing简介

OpenTracing:通过为流行的平台提供一致的,富有表现力的,==供应商中立==的API,OpenTracing使开发人员可以轻松地通过O(1)配置更改添加(或切换)跟踪实现。OpenTracing还为OSS检测和特定于平台的跟踪帮助程序库提供了通用语言。OpenTracing尽力让监控一个分布式调用过程简单化。通过OpenTracing可以快速配置一个监控系统。

OpenTracing数据模型

1.3.2 OpenTracing规范

OpenTracing 仅定义了一套开放规范和API用于在应用开发中设置埋点,实际的抓取存储日志、分析统计展现都由兼容 OpenTracing 的监控产品完成。

数据模型

OpenTracing 设计围绕两个核心概念开展:

  • Trace:一条完整的调用链,通过传递全局唯一的TraceID追踪整个调用过程。
  • Span: 记录调用链某个具体服务或方法的执行情况,通常包含调用开始时间、持续时间、调用方、被调用方、关键日志、异常、操作上下文等。

下图为SpringCloud微服务一次完整调用链,Span的因果关系图:


操作间引用

从上图数据模型间关系可以看出,一个操作可能引用0个或多个具有触发关系的操作上下文。OpenTracing定义了两种类型的引用:

  • ChildOf: 一个操作可以是另一个操作的子操作,在一个ChildOf引用中,父操作在一定程度上依赖于子操作。一下场景符合ChildOf关系:
  1. 一次Feign调用的服务端操作是客户端操作的子操作
  2. 一个表示SQL插入的操作是一个ORM save方法的子操作
  3. 一个父操作可以有多个同步进行(可能是分布式)的父操作,该父操作会在执行期限内聚合所有子操作的结果返回给用户
  • FollowsFrom: 有些父操作不以任何方式依赖它们的子操作的结果。在这种场景下,子操作仅仅由父操作触发。

二、skywalking

2.1 skywalking简介

skywalking是分布式系统的应用性能检测工具,提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。

2.1.1 skywalking功能

skywalking提供如下核心功能:

  • 多种监控手断,支持语言探针和service mesh获得监控数据
  • 支持多种语言探针,包括Java、NET、Python和NodeJS等
  • 轻量高效,无需大数据平台和服务支持
  • 模块化。UI、存储、集群管理有多种机制可选
  • 优秀的可视化解决方案。

2.1.2 skywalking架构

skywalking整体架构

skywalking整体架构可以分为四部分:

  • Skywalking Agent:语言探针,通过无侵入的方式收集链路数据,发送给OAP Server
  • Skywalking OAP: 链路数据收集器,负责接收 Agent 发送的 数据信息,然后进行分析(Analysis Core) ,存储到外部存储器( Storage ),最终提供查询( Query )功能。
  • Storage :链路追踪数据存储。目前支持 ES、MySQL、Sharding Sphere、TiDB、H2 等多种存储器。
  • Skywalking UI:可视化平台,提供一体化解决方案。

2.2 skywalking 安装部署

我们使用apache-skywalking-apm-es7-8.7.0 + ES7.x + nacos 配置中心进行skywalking环境搭建。ES7.x + nacos安装搭建这里不在赘述。

2.2.1 下载安装

step1 下载skywalking APM包

# 创建目录
$ mkdir -p /Users/skywalking
$ cd /Users/skywalking

# 下载
$ wget https://www.apache.org/dyn/closer.cgi/skywalking/java-agent/8.8.0/apache-skywalking-java-agent-8.8.0.tgz

step2 解压

# 解压
$ tar -zxvf apache-skywalking-apm-es7-8.7.0.tar.gz
$ cd  apache-skywalking-apm-bin-es7
[root@localhost apache-skywalking-apm-bin-es7]# ll

drwxrwxr-x.  9 1001 1002   176 7月  30 21:15 agent  # SkyWalking Agent语言探针插件
drwxr-xr-x.  2 root root   241 12月 24 17:13 bin # OAP Server和Web UI执行脚本
drwxr-xr-x. 11 root root  4096 12月 29 16:51 config # OAP Server配置文件
drwxr-xr-x.  2 root root    68 12月 24 17:13 config-examples
-rwxrwxr-x.  1 1001 1002 31480 7月  30 20:32 LICENSE
drwxrwxr-x.  3 1001 1002  4096 12月 24 17:13 licenses
drwxr-xr-x.  2 root root   126 1月  12 11:40 logs # 日志目录
-rwxrwxr-x.  1 1001 1002 32519 7月  30 20:32 NOTICE
drwxrwxr-x.  2 1001 1002 12288 7月  30 21:34 oap-libs # SkyWalking OAP Server
-rw-rw-r--.  1 1001 1002  1951 7月  30 20:32 README.txt
drwxr-xr-x.  3 root root    30 12月 24 17:13 tools
drwxr-xr-x.  2 root root    53 12月 24 17:31 webapp # SkyWalking UI

核心配置

OAP Server 核心配置
OAP Server 配置采用模块化配置,从集群模式、核心Collector、存储器、Collector Receiver、动态配置等提供了多种选择:

  • 集群模式(cluster)
    Collector支持集群部署,zookeeper、kubernetes(如果你的应用是部署在容器中的)、consul(GO语言开发的服务发现工具)、nacos是sw可选的集群管理工具,结合大家具体的部署方式进行选择。

cluster:
  selector: ${SW_CLUSTER:standalone}
  standalone:
  # Please check your ZooKeeper is 3.5+, However, it is also compatible with ZooKeeper 3.4.x. Replace the ZooKeeper 3.5+
  # library the oap-libs folder with your ZooKeeper 3.4.x library.
  zookeeper:
    nameSpace: ${SW_NAMESPACE:""}
    hostPort: ${SW_CLUSTER_ZK_HOST_PORT:localhost:2181}
    # Retry Policy
    baseSleepTimeMs: ${SW_CLUSTER_ZK_SLEEP_TIME:1000} # initial amount of time to wait between retries
    maxRetries: ${SW_CLUSTER_ZK_MAX_RETRIES:3} # max number of times to retry
    # Enable ACL
    enableACL: ${SW_ZK_ENABLE_ACL:false} # disable ACL in default
    schema: ${SW_ZK_SCHEMA:digest} # only support digest schema
    expression: ${SW_ZK_EXPRESSION:skywalking:skywalking}
  kubernetes:
    namespace: ${SW_CLUSTER_K8S_NAMESPACE:default}
    labelSelector: ${SW_CLUSTER_K8S_LABEL:app=collector,release=skywalking}
    uidEnvName: ${SW_CLUSTER_K8S_UID:SKYWALKING_COLLECTOR_UID}
  consul:
    serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
    # Consul cluster nodes, example: 10.0.0.1:8500,10.0.0.2:8500,10.0.0.3:8500
    hostPort: ${SW_CLUSTER_CONSUL_HOST_PORT:localhost:8500}
    aclToken: ${SW_CLUSTER_CONSUL_ACLTOKEN:""}
  etcd:
    # etcd cluster nodes, example: 10.0.0.1:2379,10.0.0.2:2379,10.0.0.3:2379
    endpoints: ${SW_CLUSTER_ETCD_ENDPOINTS:localhost:2379}
    namespace: ${SW_CLUSTER_ETCD_NAMESPACE:/skywalking}
    serviceName: ${SW_SCLUSTER_ETCD_ERVICE_NAME:"SkyWalking_OAP_Cluster"}
    authentication: ${SW_CLUSTER_ETCD_AUTHENTICATION:false}
    user: ${SW_SCLUSTER_ETCD_USER:}
    password: ${SW_SCLUSTER_ETCD_PASSWORD:}
  nacos:
    serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
    hostPort: ${SW_CLUSTER_NACOS_HOST_PORT:localhost:8848}
    # Nacos Configuration namespace
    namespace: ${SW_CLUSTER_NACOS_NAMESPACE:"public"}
    # Nacos auth username
    username: ${SW_CLUSTER_NACOS_USERNAME:""}
    password: ${SW_CLUSTER_NACOS_PASSWORD:""}
    # Nacos auth accessKey
    accessKey: ${SW_CLUSTER_NACOS_ACCESSKEY:""}
    secretKey: ${SW_CLUSTER_NACOS_SECRETKEY:""}
  • Collector Core
    Collector 核心配置主要提供通信、采样率、TTL配置:
    • 通信方式:collector提供了gRPC和HTTP两种通信方式,UI使用rest http通信,agent在大多数场景下使用grpc方式通信,在语言不支持的情况下会使用http通信。关于绑定IP和端口需要注意的一点是,通过绑定IP,agent和collector必须配置对应ip才可以正常通信。
    • downsampling: 采样汇总统计维度,会分别按照分钟、【小时、天、月】(可选)来统计各项指标数据。
    • TTL:通过设置TTL相关配置项可以对数据进行自动清理

详细配置大家可以去Skywalking官网下载介质包进行了解,这里就不贴出来了。

  • 存储器
    SW支持ES、ES7、H2、Mysql、postgrsql等,项目可以根据实际情况选择合适存储器。
storage:
  selector: elasticsearch7
  elasticsearch7:
    nameSpace: ${SW_NAMESPACE:"myyshop_monitor"}
    clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:192.168.128.34:9200}
    protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}
    connectTimeout: ${SW_STORAGE_ES_CONNECT_TIMEOUT:500}
    socketTimeout: ${SW_STORAGE_ES_SOCKET_TIMEOUT:30000}
    // 省略许多
  • Collector Receiver
    配置监控的系统中接受指标数据。支持上传符合OpenTracing规范的自定义的监控数据。
  • 配置中心
    按照Skywalking官网的客户端搭建方式,基本采取配置agent.properties文件,或者通过java -D 带参数方式(也可以直接使用环境变量进行配置),这些操作办法都属于静态配置。如果在业务高峰期,可能需要调整采样率 agent.sample_n_per_3_secs 的数值,只能通过重新启动或者agent方式更新配置信息。
    为了做到通过后台,动态控制agent端的采样率、链路跨度等配置信息,Skywalking提供了动态更新功能。Skywalking支持grpc、apollo、zookeeper、etcd、consul、k8s、nacos作为动态更新功能的配置中心。详细配置大家可以去Skywalking官网下载介质包进行了解

2.2.2 启动 OAP Server 和 Web UI

项目采用 standalone模式 + ES7 + nacos配置中心搭建 OAP Server ,配置好application.yml后便可以启动 OAP Server 和 UI。

$ cd apache-skywalking-apm-bin/bin
# 启动
$ ./startup.sh
SkyWalking OAP started successfully!
SkyWalking Web Application started successfully!

访问 http://xxxx:8081/ 即可看到如下页面:


2.3 快速集成

skywalking支持传统shell和docker模式集成

2.3.1 shell模式

skywalking agent软件包明细
shell模式需要将agent软件包拷贝到应用部署服务器,这样应用才能够使用skywalking agent。

drwxrwxr-x. 2 1001 1002     4096 12月 24 17:13 activations
drwxrwxr-x. 2 1001 1002       85 12月 24 17:13 bootstrap-plugins
drwxrwxr-x. 2 1001 1002       26 12月 24 17:13 config # 配置
drwxrwxr-x. 2 1001 1002        6 7月  30 20:35 logs # 日志
drwxrwxr-x. 2 1001 1002     4096 12月 24 17:13 optional-plugins # 可选插件,模式不会运行该目录下插件,
如果需要运行,可把对应插件拷贝到 /plugins 目录
drwxrwxr-x. 2 1001 1002       45 12月 24 17:13 optional-reporter-plugins
drwxrwxr-x. 2 1001 1002     8192 12月 24 17:13 plugins # 插件
-rw-rw-r--. 1 1001 1002 19094336 7月  30 20:35 skywalking-agent.jar  

如果应用服务器只部署一个服务,可以直接修改agent/config目录下配置,正常情况不建议这样做,而是通过设置系统变量(公共配置)或 启动命名传参(私有变量)方式。更多的变量详情,可以在[/agent/config/agent.config查看。

java启动脚本

# 通过系统变量方式设置 SkyWalking Agent 配置
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 # 配置 Collector 地址。
export SW_AGENT_SPAN_LIMIT=300# 配置链路的最大 Span 数量。默认为 300 。

# 通过启动shell命名传递SkyWalking Agent 配置
java -javaagent:D:apache-skywalking-apm-es7-8.8.0/apache-skywalking-apm-bin-es7/agentskywalking-agent.jar -Dskywalking.agent.service_name=当前项目在skywalking显示的名称 -jar spring-demo-0.0.1-SNAPSHOT.jar

2.3.2 基于docker集成

基于docker集成skywalking agent,官方提供了docker镜像代理,直接在构建应用服务镜像时使用该镜像代码作为基础镜像构建app。

FROM apache/skywalking-java-agent:8.5.0-jdk8

# ... build your java application

不过镜像代理不是官方版本,只是为了方便提供,推荐的方式还是通过源代码构建镜像。

通过源代码方式构建应用镜像

  • 1. 拷贝/agent 软件包到应用目录

软件包和pom.xml在一级目录,如下图:

  • 2. 修改软件包/agent/config/agent.config

这里仅贴出项目实际配置的,更多详情见软件包/agent/config/agent.config

agent.service_name=ds-user-center-server
# Logging file_name
logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
# Logging level
logging.level=${SW_LOGGING_LEVEL:WARN}
# 历史日志文件数量,这里配置保留一个防止日志文件过多,默认-1 不限制
logging.max_history_files=${SW_LOGGING_MAX_HISTORY_FILES:1}

ps:软件包/agent/config/agent.config配置可以不做任何配置,而是在dockerfile脚本中配置参数。

  • 3. 添加DockerFile
    这里仅贴出我自己项目的脚本,实际脚本根据加自己项目调整
FROM java:8
ADD /app.jar //

# copy arthas
# COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas

COPY agent /usr/local/agent

# 这里可以传递skywalking-agent 配置
ENTRYPOINT [ "sh", "-c", "java  -javaagent:/usr/local/agent/skywalking-agent.jar -Dskywalking.agent.service_name=yourappname -Dskywalking.collector.backend_service=xx.xx.xx.xx:11800  -Dspring.profiles.active=dev -jar /app.jar" ]
  • 4. 修改pom.xml打包程序

我们使用的是spotify maven docker插件

<build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.3</version>
                <!--加入下面两项配置-->
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>1.1.0</version>
                <executions>
                    <execution>
                        <id>build-image</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <imageName>${project.artifactId}:${project.version}</imageName>
                    <!--镜像构建成果推送进行到指定存储库-->
                    <dockerHost>http://192.168.128.20:2375</dockerHost>
                    <imageTags>
                        <imageTag>latest</imageTag>
                    </imageTags>
                    <!--dockerDirectory 参数表示使用Dockerfile构建镜像,${basedir}表示Dockerfile与pom.xml同级目录 -->
                    <dockerDirectory>${basedir}</dockerDirectory>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>

通过上述步骤构建镜像并运行服务便可以集成skywalking-agent

整合成果后效果图-拓扑图

image.png-拓扑图

三、 Spring Cloud 微服务整合skywalking及应用实战

通过上述步骤我们基本能够实际Java应用集成skywalking-agent,下面我们将开始Spring Cloud 微服务整合skywalking-agent、微服务接入skywalking日志、动态配置、基于skywalking-ui托盘图进行微服务调用关系分析、调用链分析、skywalking-agent探针优化、开启skywalking实战之旅。

3.1 微服务整合skywalking-agent

3.1.1 SpringCoud Gateway 集成skywalking-agent

微服务整合skywalking-agent按照上述Docker集成skywalking-agent步骤便能完成大多数应用的集成,只有SpringCoud Gateway需要进行额外的处理,需要将/optional-plugins可选插件包下apm-spring-cloud-gateway-xx.jar拷贝到/plugins插件包目录下,具体版本根据自己微服务版本而定,本项目使用2.2.4版本,选择apm-spring-cloud-gateway-2.1.x-plugin-8.7.0.jar即可,不要把所有版本插件全部移动到/plugins

如果SpringCoud Gateway不做上述操作,那么Gateway服务就和普通服务一样,起点就是User,而看不到Gateway作为网关路由的效果。下图为添加了apm-gateway插件后微服务全局拓扑图,可以看到前端访问微服务均是通过网关进行转发:

微服务拓扑图

3.1.2 SpringCoud 日志接入skywalking

完成skywalking-agent探针集成后,我们便可以对微服务进行调用链和拓扑图进行分析,但是我们有时还需要通过elk或者其他日志工具对调用链日志进行分析,skywalking提供无侵入式的trace IDtrace contextgRPC reporter,支持log4j、log4j2、logback。我们的项目使用logback,因此这里只介绍logback模式-Print trace ID in your logs。更多可以查看官方文档

  • step1 引入skywalking日志组件依赖
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-logback-1.x</artifactId>
            <version>8.7.0</version>
        </dependency>
  • step2 修改应用日志配置

配置layout,指定class为skywalking TraceIdPatternLogbackLayout,并且在模板中增加tid的锚点。

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <springProperty scope="context" name="profilesActive" source="spring.profiles.active"/>

    <appender name="consoleApp" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <!--SW layout 配置 -->
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <Pattern> %date{yyyy-MM-dd HH:mm:ss.SSS} [%tid] %-5level[%thread]%logger{56}.%method:%L -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>
    <appender name="fileInfoApp" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>DENY</onMatch>
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
            <Pattern> %date{yyyy-MM-dd HH:mm:ss.SSS} [%tid] %-5level[%thread]%logger{56}.%method:%L -%msg%n</Pattern>
        </layout>
        <!-- 滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 路径 -->
            <fileNamePattern>logs/${springAppName}.info.%d.log</fileNamePattern>
        </rollingPolicy>
    </appender>

    <appender name="fileErrorApp" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
            <Pattern> %date{yyyy-MM-dd HH:mm:ss.SSS} [%tid] %-5level[%thread]%logger{56}.%method:%L -%msg%n</Pattern>
        </layout>
        <!-- 设置滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 路径 -->
            <fileNamePattern>logs/${springAppName}.err.%d.log</fileNamePattern>

            <!-- 控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每个月滚动,
            且<maxHistory> 是1,则只保存最近1个月的文件,删除之前的旧文件 -->
            <MaxHistory>1</MaxHistory>

        </rollingPolicy>
    </appender>
    <springProfile name="dev">
        <root level="INFO">
            <appender-ref ref="consoleApp"/>
            <appender-ref ref="fileInfoApp"/>
            <appender-ref ref="fileErrorApp"/>
        </root>
    </springProfile>
    <springProfile name="test">
        <root level="INFO">
            <appender-ref ref="consoleApp"/>
        </root>
    </springProfile>
    <springProfile name="prod">
        <root level="INFO">
            <appender-ref ref="consoleApp"/>
        </root>
    </springProfile>
</configuration>

修改完配置,启动应用,可以看到如下效果:当应用激活了SW agent,外部请求日志打印会携带tid,而内部请求则打印N/A。

2022-02-07 00:00:00.031 [TID:N/A] INFO [xxl-rpc, EmbedServer bizThreadPool-71423529]com.xxl.job.core.executor.XxlJobExecutor.registJobThread:159 ->>>>>>>>>>> xxl-job regist JobThread success, jobId:144, handler:com.xxl.job.core.handler.impl.MethodJobHandler@c8567c9[class com.xxxx.saas.dsuserserver.job.VipActivePackagesExpiredStateJob$$EnhancerBySpringCGLIB$$72fe4b40#vipActivePackagesExpiredState]
2022-02-07 00:00:00.036 [TID:3344006a6a504161b199c17d5778a36d.3192.16441632000360001] INFO [Thread-1559]c.m.s.dsuserserver.job.VipActivePackagesExpiredStateJob.vipActivePackagesExpiredState:42 -================vipActivePackagesExpiredState start:1644163200036================
2022-02-07 00:00:00.112 [TID:3344006a6a504161b199c17d5778a36d.3192.16441632000360001] INFO [Thread-1559]c.m.s.dsuserserver.job.VipActivePackagesExpiredStateJob.vipActivePackagesExpiredState:46 -================vipActivePackagesExpiredState end:1644163200112================
2022-02-07 00:01:33.130 [TID:N/A] INFO [Thread-1559]com.xxl.job.core.thread.JobThread.run:212 ->>>>>>>>>>> xxl-job JobThread stoped, hashCode:Thread[Thread-1559,10,main]
2022-02-07 10:34:52.757 [TID:d9bb5cb491e34b5ca945c591a326ed8b.69.16442012921820337] INFO [http-nio-7004-exec-8]c.d.saas.common.feign.interceptor.MyOkhttpInterceptor.intercept:23 -OkHttp Feign 发送请求 [http://192.168.128.20:7001/oauth/token?password=a123456&grant_type=password&userType=1&username=sujingjun%40dhgate.com] on [null]

使用日志追踪打印的tid,在SW UI 追踪模块可以追踪到对应的请求链路,如下图:


image.png

源码分析

  • TraceIdPatternLogbackLayout:TraceIdPatternLogbackLayout继承ch.qos.logback.classic.PatternLayout,添加了LogbackPatternConverter和LogbackSkyWalkingContextPatternConverter转换器,支持%tid占位符。
ublic class TraceIdPatternLogbackLayout extends PatternLayout {
    public TraceIdPatternLogbackLayout() {
    }

    static {
        defaultConverterMap.put("tid", LogbackPatternConverter.class.getName());
        defaultConverterMap.put("sw_ctx", LogbackSkyWalkingContextPatternConverter.class.getName());
    }
}
// 这里实际仅是一个入口程序,实际日志打印实现是agent-activation包下定义的切面和拦截器处理
public class LogbackPatternConverter extends ClassicConverter {
    public LogbackPatternConverter() {
    }

    public String convert(ILoggingEvent iLoggingEvent) {
        return "TID: N/A";
    }
}

public class LogbackPatternConverterActivation extends ClassInstanceMethodsEnhancePluginDefine {

    public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.toolkit.activation.log.logback.v1.x.PrintTraceIdInterceptor";
    public static final String ENHANCE_CLASS = "org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackPatternConverter";
    public static final String ENHANCE_METHOD = "convert";

    /**
     * @return the target class, which needs active.
     */
    @Override
    protected ClassMatch enhanceClass() {
        return byName(ENHANCE_CLASS);
    }

    /**
     * @return null, no need to intercept constructor of enhance class.
     */
    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return null;
    }

    /**
     * @return the collection of {@link StaticMethodsInterceptPoint}, represent the intercepted methods and their
     * interceptors.
     */
    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named(ENHANCE_METHOD).and(takesArgumentWithType(0, "ch.qos.logback.classic.spi.ILoggingEvent"));
                }

                @Override
                public String getMethodsInterceptor() {
                    return INTERCEPT_CLASS;
                }

                @Override
                public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }
}
  • PrintTraceIdInterceptor: PrintTraceIdInterceptor是%tid占位符转换的实际处理拦截器,*.afterMethod() 方法调用 ContextManager#getGlobalTraceId() 方法,使用全局链路追踪编号。
public class PrintTraceIdInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
        MethodInterceptResult result) throws Throwable {

    }

    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
        Object ret) throws Throwable {
        if (!ContextManager.isActive()) {
            if (allArguments[0] instanceof EnhancedInstance) {
                SkyWalkingContext skyWalkingContext = (SkyWalkingContext) ((EnhancedInstance) allArguments[0]).getSkyWalkingDynamicField();
                if (skyWalkingContext != null) {
                    return "TID:" + skyWalkingContext.getTraceId();
                }
            }
        }
        return "TID:" + ContextManager.getGlobalTraceId();
    }

    @Override
    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, Throwable t) {

    }
}

更多源码详情参考官方https://github.com/apache/skywalking


3.1.3 忽略追踪某项节点url

SW支持忽略特点节点url,要支持节点忽略功能需要将apm-trace-ignore-plugin-8.7.0.jar插件包拷贝到../plugins目录下,同时需要在../config目录下增加配置文件apm-trace-ignore-plugin.config,并加入trace.ignore_path=${SW_AGENT_TRACE_IGNORE_PATH:/api-docs/**} 为例,来忽略请求url的追踪,示例如下:

trace.ignore_path=/api-docs/**,/actuator/**

3.2 客户端动态配置

按照Skywalking官网的客户端搭建方式,基本采取配置文件,或者通过java -D 带参数方式(也可以直接使用环境变量进行配置),这些操作办法都属于静态配置。如果在生产环境需要调整采样率、忽略url等,只能通过重新启动agent方式更新配置信息。

Skywalking官方提供了动态更新功能,可以结合配置中心实现后台动态更新agent采样率、忽略url、调用链深度等配置。

3.2.1 动态配置说明

SW 支持以下动态配置:

配置key 参数描述 参数示例
agent-analyzer.default.slowDBAccessThreshold 慢sql的阈值,覆盖application.yml中receiver-trace/default/slowDBAccessThreshold default:200,mongodb:50
agent-analyzer.default.uninstrumentedGateways 网关配置 参数参考gateways.yml
alarm.default.alarm-settings 告警配置 参考alarm-settings.yml
core.default.endpoint-name-grouping 端点分组配置 参考service-apdex-threshold.yml
core.default.log4j-xml log4j配置 参考log4j2.xml
configuration-discovery.default.agentConfigurations ConfigurationDiscovery 配置 参考configuration-discovery.md

更多动态配置说明参考Dynamic Configuration

3.2.2 集成nacos实现SW动态配置

SW动态配置依赖上游配置服务,因此默认是关闭的,在2.2 核心配置已经介绍过配置中心配置说明,这里不再赘述。我们直接从nacos集成步骤开始

  • step1 nacos配置中心安装部署

我们直接使用SpringCloud nacos配置中心作为SW配置中心不再额外部署nacos,nacos安装部署可以参考官方https://nacos.io/zh-cn/docs/deployment.html

  • 修改OAP Server

修改OAP Server application.yml 开启动态配置并使用nacos作为配置中心


configuration:
  selector: nacos #开启开启动态配置并使用nacos作为配置中心
  nacos:
    # Nacos Server Host
    serverAddr: ${SW_CONFIG_NACOS_SERVER_ADDR:192.168.128.20}
    # Nacos Server Port
    port: ${SW_CONFIG_NACOS_SERVER_PORT:8848}
    # Nacos Configuration Group
    group: ${SW_CONFIG_NACOS_SERVER_GROUP:skywalking}
    # Nacos Configuration namespace
    namespace: ${SW_CONFIG_NACOS_SERVER_NAMESPACE:1c53b59e-9f3d-44a4-b2d7-5faaea25b3c6}
    # Unit seconds, sync period. Default fetch every 60 seconds.
    period: ${SW_CONFIG_NACOS_PERIOD:60}
    # Nacos auth username
    username: ${SW_CONFIG_NACOS_USERNAME:""}
    password: ${SW_CONFIG_NACOS_PASSWORD:""}
    # Nacos auth accessKey
    accessKey: ${SW_CONFIG_NACOS_ACCESSKEY:""}
    secretKey: ${SW_CONFIG_NACOS_SECRETKEY:""}

nacos动态配置结果如下:

nacos动态配置
nacos动态配置

3.2.3 ConfigurationDiscovery 配置举例说明

ConfigurationDiscovery 配置了服务发现配置,包含采样率、不记录链路信息url后缀、不记录链路信息url、链路最大跨度。ConfigurationDiscovery 配置示例如下:

configurations:
    ds-user-center-server: 
        agent.trace.ignore_path: Mysql/**,Lettuce/**,/941124188381/*,Lettuce/EXISTS/**,MongoDB/**
    uaa-center-server: 
        agent.trace.ignore_path: /api-docs/**,/actuator/**,Mysql/**,Lettuce/**,Lettuce/EXISTS/**
    gateway-server: 
        agent.trace.ignore_path: /api-docs/**,/actuator/**

ConfigurationDiscovery 配置模板

ConfigurationDiscovery 按服务维度进行配置

configurations:
  //service name
  serviceA:
    // Configurations of service A
    // Key and Value are determined by the agent side.
    // Check the agent setup doc for all available configurations.
    key1: value1
    key2: value2
    ...
  serviceB:
    ...

ConfigurationDiscovery 配置说明

key 参数描述 参数示例 依赖插件
agent.sample_n_per_3_secs 每3秒采集链路数据,-1表示全部收集 -1 -
agent.ignore_suffix 不记录链路信息url后缀(第一个节点开始算起),如果多个,采用逗号,隔开 .txt,.log -
agent.trace.ignore_path 记录调用链的路径,多个可以采用逗号,隔开 /your/path/1/,/your/path/2/ apm-trace-ignore-plugin
agent.span_limit_per_segment 链路最大跨度 300 -

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容