[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可以快速配置一个监控系统。
1.3.2 OpenTracing规范
OpenTracing 仅定义了一套开放规范和API用于在应用开发中设置埋点,实际的抓取存储日志、分析统计展现都由兼容 OpenTracing 的监控产品完成。
数据模型
OpenTracing 设计围绕两个核心概念开展:
- Trace:一条完整的调用链,通过传递全局唯一的TraceID追踪整个调用过程。
- Span: 记录调用链某个具体服务或方法的执行情况,通常包含调用开始时间、持续时间、调用方、被调用方、关键日志、异常、操作上下文等。
下图为SpringCloud微服务一次完整调用链,Span的因果关系图:
操作间引用
从上图数据模型间关系可以看出,一个操作可能引用0个或多个具有触发关系的操作上下文。OpenTracing定义了两种类型的引用:
- ChildOf: 一个操作可以是另一个操作的子操作,在一个ChildOf引用中,父操作在一定程度上依赖于子操作。一下场景符合ChildOf关系:
- 一次Feign调用的服务端操作是客户端操作的子操作
- 一个表示SQL插入的操作是一个ORM save方法的子操作
- 一个父操作可以有多个同步进行(可能是分布式)的父操作,该父操作会在执行期限内聚合所有子操作的结果返回给用户
- FollowsFrom: 有些父操作不以任何方式依赖它们的子操作的结果。在这种场景下,子操作仅仅由父操作触发。
二、skywalking
2.1 skywalking简介
skywalking是分布式系统的应用性能检测工具,提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。
2.1.1 skywalking功能
skywalking提供如下核心功能:
- 多种监控手断,支持语言探针和service mesh获得监控数据
- 支持多种语言探针,包括Java、NET、Python和NodeJS等
- 轻量高效,无需大数据平台和服务支持
- 模块化。UI、存储、集群管理有多种机制可选
- 优秀的可视化解决方案。
2.1.2 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
三、 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 ID、trace context、gRPC 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 追踪模块可以追踪到对应的请求链路,如下图:
源码分析
- 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";
}
}
- LogbackPatternConverterActivation: LogbackPatternConverterActivation定义了%tid占位符转换器方法切面 和 拦截器。
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动态配置结果如下:
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 | - |