链路追踪Skywalking

1,203 阅读13分钟

简介

skywalking是分布式系统的应用程序性能监视工具,2015年由吴晟开源 2017年加入Apache孵化器

专为微服务、云原生架构和基于容器(Docker、k8s)架构而设计,它是一款优秀的APM(Application Performance Management)工具,包括了分布式链路性能指标分析应用和服务依赖分析等强大的监控功能。官网地址:skywalking.apache.org

链路追踪框架对比

项目CatZipkinSkywalking
调用可视化
聚合报表非常丰富较丰富
服务依赖图简单简单
埋点方式侵入式侵入式非侵入,字节码加强
VM监控指标
支持语言java/.net丰富java/.net/nodejs/php/go
存储机制mysql/本地文件/HDFS内存/ES/mysqlh2/mysql/es/tidb
社区支持国内国外Apache支持
使用案例美团、携程、陆金所京东、阿里华为、小米、当当、微众银行
APM
开发基础eBay calGoogle DapperGoogle Dapper
是否支持webflux
github stars12.3K12.2K18K

主要功能特性

1.多种监控手段,可用通过语言探针和service mesh获取监控的数据

2.支持多种语言自动探针java、.net、nodejs

3.轻量高效,无需大数据平台和大量服务器资源

4.模块化,UI、存储、集群管理都有多种机制可选

5.支持告警,优秀的可视化解决方案

6.性能好,针对单实例5000tps的应用,在全量采集的情况下只增加10%的CPU开销

7.采用探针技术,在使用过程中完全是0代码、无入侵,分布式自动采集与监控系统运行

8.支持自动及手动探针

自动探针:java支持的中间件、框架与类库列表

手动探针:OpenTrackingApi、@Trace注解、trackId集成到日志中

环境搭建部署

整体结构介绍

image.png

  • skywalking agent和业务系统绑定在一起,负责收集各种监控数据

  • skywalking oapservice是负责接收skywalking agent上报的监控数据处理后存储在数据库中

    接收skywalking webapp的前端请求从数据库查询数据并返回数据给前端渲染

    skywalking oapservice通常是以集群的形式存在

  • skywalking webapp前端界面用于展示数据

  • 数据库可以是mysql、es、tidb等

单机搭建skywalking

下载地址:skywalking.apache.org/downloads

image.png

下载完后我们进行解压看一下它的目录结构

  • webapp:UI前端(web监控页面)的jar包和配置文件

  • oap-libs:后台应用的jar包,以及他的依赖jar包;里边有一个server-starter-*.jar就是启动程序

  • config:启动后台应用程序使用的配置文件

  • bin:各种启动脚本,一般使用脚本startup.*来启动web页面和后台应用

    • oapService.*:后台程序启动脚本(使用的是默认模式启动,还支持其他模式。各模式区别见启动模式)
    • oapServiceInit.*:使用init模式启动,在此模式下oap服务器启动以执行初始化工作然后退出
    • oapServiceNoInit.*:使用no init模式启动,在此模式下oap服务不进行初始化
    • webappService.*:UI前端的启动脚本
    • startup.*:组合脚本,同时启动oapService、webappService脚本
  • agent

    • skywalking-agent.jar:代理服务jar包
    • config:代理服务启动时使用的配置文件
    • plugins:包含多个插件,代理服务启动时会加载该目录的所有插件(实际是各种jar)
    • optional-plugins:可选插件,当需要支持某种功能时比如SpringCloud-Gateway,则需要拷到plugins目录下

启动服务

进到bin目录下看到点击startup就可以同时启动oapService(端口12800)、webappService(端口8080)

浏览器地址栏输入: http://127.0.0.1:8080 就可以看到skywalking的UI界面了

image.png

接入持久化数据库

oapService默认使用的存储模式是h2内存数据库,这样会导致内存占用过高且重启后丢失数据

丢失数据问题倒不是很大因为这些监控数据都是希望看到实时性的,顶多说三天前的数据还跟踪一下

总不能把上个月的监控数据拿出来排查问题吧,但是使用内存来做数据存储方案肯定会导致内存爆满

这个时候就只能重启了,重启完就丢失数据,隔三差五的去重启也不现实。所以我们得做持久化处理

官方支持很多种持久化方案比较推荐使用ES,但是为了方便可以基于mysql数据库来进行存储

好比刚刚说的老数据一般不会查询可以每个月定时清一次数据,这种量级mysql还是扛得住的实在不行就tidb

1.打开skywalking文件夹进入到config目录找到application.yml文件进行编辑

image.png

2.在application.yml文件中找到storage存储配置可以看得到默认配置是h2内存数据库

image.png

3.继续往下翻到mysql的配置进行修改数据库连接信息,往上翻到storage.selector将默认配置改为mysql

image.png

image.png

4.进入skywalking文件夹中的bin目录双击startup.bat启动程序

  • 这时候发现oap服务自动停掉了因为这是skywalking的一个小坑,我们可以到logs日志文件中查看异常信息

    image.png

    发现是少了个mysql驱动包,这时候我们将mysql驱动包放入oap-libs文件夹中,重新双击startup.bat启动程序

    image.png

    启动后等待两分钟后看下skywalking给数据库初始化创建的表

    image.png

    这时候就接入mysql数据库成功了

    5.打开浏览器输入http://127.0.0.1:8080查看UI界面

    image.png

集群搭建skywalking

在大多数生产环境中,后端应用需要支持高吞吐量并且支持高可用来保证服务的稳定,所以应用始终需要在生产环境进行集群管理

skywalking集群是将oap作为一个服务注册到注册中心,只要skywalking oap服务没有全部宕机保证有一个oap实例在运行就能进行跟踪与监控

搭建skywalking 集群需要环境如下:

服务最小实例数备注
注册中心1nacos、zookeeper、kubernetes、consul、etcd
数据库1elasticsearch、mysql、tidb、influxdb、postgresql
skywalking oap2建议3个
skywalking webapp1webapp服务也可以用nginx代理统一入口进行集群

1.进入到oap的config目录修改application.yml配置文件

image.png

2.打开配置文件找到cluster集群模式配置

image.png

3.修改两个端口单机模拟一个伪集群:oap实例端口/oap监控数据收集端口

image.png

启动三个实例端口分别为:12800/11800、12700/11700、12600/11600

修改一次就双击一次/bin/oapServiceNoInit.bat(不作初始化操作)

image.png

image.png

4.修改skywalking webapp服务的webapp.yml文件的listOfServers

image.png

5.启动skywalking webapp服务 /bin/webappService.bat

image.png

image.png

能够正常访问查看日志没有异常信息则证明搭建成功!

skywalking接入和追踪

接入

接下来将搭建skywalking的客户端也就是将微服务接入到skywalking当中

网关是微服务的统一入口那么我们就从网关gateway开始接入,以下是接入skywalking重要的三个参数

参数说明默认
skywalking.agent.service_name客户端服务名一般都用spring.application.name方便查看
skywalking.collector.backend_serviceskywalking aop collerctor地址skywalking oap 收集器地址,集群则用逗号分隔
-javaagent:skywalking-agent 磁盘路径D:\apache-skywalking-apm-es7-8.6.0\agent\skywalking-agent.jar
-Dskywalking.agent.service_name=gateway
-Dskywalking.collector.backend_service=127.0.0.1:11800
-javaagent:D:\apache-skywalking-apm-es7-8.6.0\agent\skywalking-agent.jar

步骤一:让skywalking支持gateway

进入到D:\apache-skywalking-apm-es7-8.6.0\agent\optional-plugins目录

将其中的apm-spring-cloud-gateway-2.1.x-plugin-8.6.0.jar

拷贝到D:\apache-skywalking-apm-es7-8.6.0\agent\plugins

如下图所示

image.png

image.png

步骤二:启动skywalking

双击D:\apache-skywalking-apm-es7-8.6.0\bin\startup.bat,弹出两个两个窗口即可

image.png

步骤三:idea中设置jvm启动参数

image.png

image.png

步骤四:正常启动应用程序

image.png

当看到这行日志执行就证明加载了skywalking的agent,随后到skywalking的web前端界面的仪表盘和拓补图看一下gateway接入后的效果

image.pngimage.png

追踪

嗯!的确是显示了服务信息,对于微服务而言gateway本身只是在做转发到最终还是交由具体的微服务进行处理

为了让整个链路看得更清晰还需要将具体的服务也接入到skywalking当中去,和之前gateway接入的步骤一样设置jvm参数即可

image.png

将skywalking的服务名改成具体的服务名后开始启动

image.png image.png image.png image.png

我们将gateway和base-service注册到了skywalking中,那接下来便看一看它的链路追踪效果

  • 通过网关请求一次接口

  • 控制台查看点击链路,按照开始时间进行排序

  • 通过表格的方式查看

  • 选中mysql执行的链路查看执行的sql语句

  • 拓扑图呈现的关系

    image.png

    由用户先请求到gateway后再转发到了base-service而它们都使用了同一个redis,base-service还连接了mysql数据库

自定义链路追踪

如果我们希望对项目中的业务方法实现链路追踪,方便排查问题,可以导入如下依赖版本号尽量和skywalking保持一致

它可以将任意业务方法加入到skywalkingUI界面中的链路追踪,可以看到自定义的链路节点耗费时间并包括参数返回值等常规信息

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

java代码案例

image.png

请求访问自定义链路追踪接口后从链路面板查看刚才的请求信息

image.png

选中我们自定义的链路追踪节点,查看详细信息

image.png

注解作用参数说明
@Trace标记需要追踪的链路节点operationName默认是方法全名(包名.类名.方法名字)
@Tags组合多个@Tag标签数组
@Tag自定义标签key=自定义名称 value=值,arg表示参数数组,returnedObj表示返回值
@TraceCrossThread跨线程监控无参
@IgnoredException忽略异常无参

只要在方法上贴上@Trace、@Tags、@Tag就可以自定义链路信息,这样对于开发后期的问题排查也是十分友好的了

当然里面还有@TraceCrossThread、@IgnoredException的使用就不一一赘述了

值得一提的是skywalking默认只监控单线程链路如果开异步线程还需要监控的话则配合@TraceCrossThread注解支持

小结

对于linux环境或者是打成jar包以后的接入也是在将jvm启动时加入前面三个重要的参数即可

注意的是**-javaagent参数写的是skywalking-agent.jar所在的具体位置**

到这就完成skywalking的接入和追踪,也验证了它的确可以看到全链路调用关系各个步骤总共耗费的时间、自身耗费的时间

从而对于慢响应的请求进行链路分析帮助开发者更方便去排查问题,性能调优,接下来便详细介绍skywalking各项功能的使用

仪表盘

APM

Global全局维度

1641884645(1).jpg

图表描述
Services load每个服务每分钟请求次数
Slow Services每个服务耗时最慢的一次(单位ms)
Un-Health services(Apdex)每个服务健康指数:0-1(1为非常健康)
Slow Endpoints所有服务慢请求排行榜
Global Response Latency百分比的响应时间(单位ms)
Global Heatmap服务响应时间热力分布图(根据响应时间的数量显示颜色深度)

Service服务维度

image.png

图表描述
Service Apdex(数字)服务评分:0-1(1为非常健康)
Service Apdex(折线图)服务不同时间评分
Service Throughput服务吞吐量
Service Avg Response Time服务平均响应时间(单位ms)
Service Response Time Percentile服务百分比响应时间
Successful Rate(数字)服务请求成功率
Successful Rate(折线图)服务不同时间请求成功率
Services load(数字)服务每分钟请求数
Services load(折线图)服务不同时间每分钟请求数
Service Instances Load每个服务实例的每分钟请求数
Slow Service Instance每个服务实例的最大延时
Service Instance Successful Rate每个服务实例的请求成功率

Instance实例维度

image.png

图表描述
Service Instance Load每分钟请求数
Service Instance Throughput吞吐量
Service Instance Successful Rate请求成功率
Service Instance Latency响应延时
JVM CPU (Java Service)jvm占用CPU百分比
JVM Memory (Java Service)jvm内存占用大小
JVM GC Timejvm垃圾回收时间(YGC和OGC)
JVM GC Countjvm垃圾回收次数(YGC和OGC)
JVM Thread Count (Java Service)jvm创建线程数量

Endpoint端点维度

image.png

图表描述
Endpoint Load in Current Service每个端点每分钟请求数
Slow Endpoints in Current Service每个端点最慢请求
Successful Rate in Current Service每个端点成功率
Endpoint Load当前端点每个时间段请求数
Endpoint Avg Response Time当前端点每个时间段平均响应时间
Endpoint Response Time Percentile当前端点每个时间段响应时间占比
Endpoint Successful Rate当前端点每个时间段请求成功率

DataSource

image.png

图表描述
Database Avg Response Time数据库事件平均响应时间(单位ms)
Database Access Successful Rate数据库访问成功率
Database Traffic数据库每分钟请求数(CPM)
Database Access Latency Percentile数据库不同比例响应时间(单位ms)
Slow Statements前N个慢查询(单位ms)
All Database Loads所有数据库中CPM排名
Un-Health Databases (Successful Rate)所有数据库健康排名、请求成功率排名

性能剖析

性能监测分析:根据服务、端点、规则建立性能监测任务后会自动记录并生成剖析结果

主要用于慢响应请求排查

  • 新建监控任务

  • 浏览器发起请求 image.png

  • 查看采样结果

    image.png

  • 性能分析

    image.png

    查看线程栈详细项链

日志

skywalking可以集成服务日志,方便在skywalking的web控制台中结合日志排查问题

  • 添加依赖包

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

    导入的版本号尽量和skywalking保持一致,这里我用的是logback日志框架

    如果是其他日志框架也会有其对应的依赖包如:log4j、log4j2这些

  • logback-spring.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!-- 日志输出格式 -->
        <property name="log.pattern"
                  value="%d{yyyy-MM-DD HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{50} - [%method,%line] - %msg%n"/>
    ​
        <!-- 控制台输出 -->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>${log.pattern}</pattern>
            </encoder>
        </appender>
    ​
    ​
        <!--skywalking 日志收集器-->
        <appender name="skywalking" 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>${log.pattern}</pattern>
                </layout>
            </encoder>
        </appender>
    ​
        <root level="info">
            <appender-ref ref="console"/>
            <appender-ref ref="skywalking"/>
        </root>
    </configuration>
    
  • 请求接口看控制台日志

    image.png

    可以发现请求中带有TID(traceID)

  • 查看skywalking日志

    image.png

告警

image.png

告警功能其核心由一组规则驱动而来,这些规则定义在config/alarm-settings.yml文件中

主要分为告警规则和网络钩子两部分

告警规则

告警规则是定义什么条件下触发度量警报,默认在config/alarm-settings.yml文件中定义了一些常用的告警规则

image.png

过去3分钟内服务平均响应时间超过1秒、过去2分钟服务成功率低于80%

过去3分钟内服务响应时间超过1秒的百分比

服务实例在过去2分钟内平均响应时间超过1秒并且实例名称与正则表达式匹配

过去2分钟内端点平均响应时间超过1秒

过去2分钟内数据库访问平均响应时间超过1秒

过去2分钟内端点关系平均响应时间超过1秒

规则配置项术语说明
rule name规则名称也是告警信息中唯一标识,必须以_rule为后缀
metrics name度量名称oal脚本中的度量名称
include names包含服务包含哪些服务名,默认为全部(可选参数)
exclude names排除服务排除哪些服务名,默认为空(可选参数)
op操作符目前只支持>、<、=
threshold阈值
period时期多久检查一次当前的指标数据是否符合告警规则,单位分钟
count次数达到多少次后,发送告警消息
silence-period安静时期在多久之内,忽略相同的告警消息
message告警消息告警消息内容

网络钩子(webhook)

webhook可以理解为是一种web层面的回调机制,通常由一些事件触发回调只不过是发生在网络层面

它可以往任意接口发送请求但是请求的json格式我们要关心,每个版本都有所不同尽量在skywalking中

的源码翻看 /alarm/AlarmMessage

image.png

image.png

image.png

[{    "scopeId": 1,    "scope": "SERVICE",    "name": "serviceA",    "id0": 12,    "id1": 0,    "ruleName": "service_resp_time_rule",    "alarmMessage": "alarmMessage xxxx",    "startTime": 1560524171000}, {    "scopeId": 1,    "scope": "SERVICE",    "name": "serviceB",    "id0": 23,    "id1": 0,    "ruleName": "service_resp_time_rule",    "alarmMessage": "alarmMessage yyy",    "startTime": 1560524171000}]
字段说明
name服务名
id0scope实体的 ID
id1保留字段,目前暂未使用
ruleName告警规则名称
alarmMessage告警消息内容
startTime告警时间,格式为时间戳
  • 编写回调接口

  • 配置回调地址