简介
skywalking是分布式系统的应用程序性能监视工具,2015年由吴晟开源 2017年加入Apache孵化器
专为微服务、云原生架构和基于容器(Docker、k8s)架构而设计,它是一款优秀的APM(Application Performance Management)工具,包括了分布式链路、性能指标分析、应用和服务依赖分析等强大的监控功能。官网地址:skywalking.apache.org
链路追踪框架对比
| 项目 | Cat | Zipkin | Skywalking |
|---|---|---|---|
| 调用可视化 | 有 | 有 | 有 |
| 聚合报表 | 非常丰富 | 少 | 较丰富 |
| 服务依赖图 | 简单 | 简单 | 好 |
| 埋点方式 | 侵入式 | 侵入式 | 非侵入,字节码加强 |
| VM监控指标 | 好 | 无 | 有 |
| 支持语言 | java/.net | 丰富 | java/.net/nodejs/php/go |
| 存储机制 | mysql/本地文件/HDFS | 内存/ES/mysql | h2/mysql/es/tidb |
| 社区支持 | 国内 | 国外 | Apache支持 |
| 使用案例 | 美团、携程、陆金所 | 京东、阿里 | 华为、小米、当当、微众银行 |
| APM | 是 | 否 | 是 |
| 开发基础 | eBay cal | Google Dapper | Google Dapper |
| 是否支持webflux | 否 | 是 | 是 |
| github stars | 12.3K | 12.2K | 18K |
主要功能特性
1.多种监控手段,可用通过语言探针和service mesh获取监控的数据
2.支持多种语言自动探针java、.net、nodejs
3.轻量高效,无需大数据平台和大量服务器资源
4.模块化,UI、存储、集群管理都有多种机制可选
5.支持告警,优秀的可视化解决方案
6.性能好,针对单实例5000tps的应用,在全量采集的情况下只增加10%的CPU开销
7.采用探针技术,在使用过程中完全是0代码、无入侵,分布式自动采集与监控系统运行
8.支持自动及手动探针
自动探针:java支持的中间件、框架与类库列表
手动探针:OpenTrackingApi、@Trace注解、trackId集成到日志中
环境搭建部署
整体结构介绍
-
skywalking agent和业务系统绑定在一起,负责收集各种监控数据
-
skywalking oapservice是负责接收skywalking agent上报的监控数据处理后存储在数据库中
接收skywalking webapp的前端请求从数据库查询数据并返回数据给前端渲染
skywalking oapservice通常是以集群的形式存在
-
skywalking webapp前端界面用于展示数据
-
数据库可以是mysql、es、tidb等
单机搭建skywalking
下载地址:skywalking.apache.org/downloads
下载完后我们进行解压看一下它的目录结构
-
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界面了
接入持久化数据库
oapService默认使用的存储模式是h2内存数据库,这样会导致内存占用过高且重启后丢失数据
丢失数据问题倒不是很大因为这些监控数据都是希望看到实时性的,顶多说三天前的数据还跟踪一下
总不能把上个月的监控数据拿出来排查问题吧,但是使用内存来做数据存储方案肯定会导致内存爆满
这个时候就只能重启了,重启完就丢失数据,隔三差五的去重启也不现实。所以我们得做持久化处理
官方支持很多种持久化方案比较推荐使用ES,但是为了方便可以基于mysql数据库来进行存储
好比刚刚说的老数据一般不会查询可以每个月定时清一次数据,这种量级mysql还是扛得住的实在不行就tidb
1.打开skywalking文件夹进入到config目录找到application.yml文件进行编辑
2.在application.yml文件中找到storage存储配置可以看得到默认配置是h2内存数据库
3.继续往下翻到mysql的配置进行修改数据库连接信息,往上翻到storage.selector将默认配置改为mysql
4.进入skywalking文件夹中的bin目录双击startup.bat启动程序
-
这时候发现oap服务自动停掉了因为这是skywalking的一个小坑,我们可以到logs日志文件中查看异常信息
发现是少了个mysql驱动包,这时候我们将mysql驱动包放入oap-libs文件夹中,重新双击startup.bat启动程序
启动后等待两分钟后看下skywalking给数据库初始化创建的表
这时候就接入mysql数据库成功了
5.打开浏览器输入http://127.0.0.1:8080查看UI界面
集群搭建skywalking
在大多数生产环境中,后端应用需要支持高吞吐量并且支持高可用来保证服务的稳定,所以应用始终需要在生产环境进行集群管理
skywalking集群是将oap作为一个服务注册到注册中心,只要skywalking oap服务没有全部宕机保证有一个oap实例在运行就能进行跟踪与监控
搭建skywalking 集群需要环境如下:
| 服务 | 最小实例数 | 备注 |
|---|---|---|
| 注册中心 | 1 | nacos、zookeeper、kubernetes、consul、etcd |
| 数据库 | 1 | elasticsearch、mysql、tidb、influxdb、postgresql |
| skywalking oap | 2 | 建议3个 |
| skywalking webapp | 1 | webapp服务也可以用nginx代理统一入口进行集群 |
1.进入到oap的config目录修改application.yml配置文件
2.打开配置文件找到cluster集群模式配置
3.修改两个端口单机模拟一个伪集群:oap实例端口/oap监控数据收集端口
启动三个实例端口分别为:12800/11800、12700/11700、12600/11600
修改一次就双击一次/bin/oapServiceNoInit.bat(不作初始化操作)
4.修改skywalking webapp服务的webapp.yml文件的listOfServers
5.启动skywalking webapp服务 /bin/webappService.bat
能够正常访问查看日志没有异常信息则证明搭建成功!
skywalking接入和追踪
接入
接下来将搭建skywalking的客户端也就是将微服务接入到skywalking当中
网关是微服务的统一入口那么我们就从网关gateway开始接入,以下是接入skywalking重要的三个参数
| 参数 | 说明 | 默认 |
|---|---|---|
| skywalking.agent.service_name | 客户端服务名 | 一般都用spring.application.name方便查看 |
| skywalking.collector.backend_service | skywalking 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
如下图所示
步骤二:启动skywalking
双击D:\apache-skywalking-apm-es7-8.6.0\bin\startup.bat,弹出两个两个窗口即可
步骤三:idea中设置jvm启动参数
步骤四:正常启动应用程序
当看到这行日志执行就证明加载了skywalking的agent,随后到skywalking的web前端界面的仪表盘和拓补图看一下gateway接入后的效果
追踪
嗯!的确是显示了服务信息,对于微服务而言gateway本身只是在做转发到最终还是交由具体的微服务进行处理
为了让整个链路看得更清晰还需要将具体的服务也接入到skywalking当中去,和之前gateway接入的步骤一样设置jvm参数即可
将skywalking的服务名改成具体的服务名后开始启动
我们将gateway和base-service注册到了skywalking中,那接下来便看一看它的链路追踪效果
-
通过网关请求一次接口
-
控制台查看点击链路,按照开始时间进行排序
-
通过表格的方式查看
-
选中mysql执行的链路查看执行的sql语句
-
拓扑图呈现的关系
由用户先请求到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代码案例
请求访问自定义链路追踪接口后从链路面板查看刚才的请求信息
选中我们自定义的链路追踪节点,查看详细信息
| 注解 | 作用 | 参数说明 |
|---|---|---|
| @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全局维度
| 图表 | 描述 |
|---|---|
| Services load | 每个服务每分钟请求次数 |
| Slow Services | 每个服务耗时最慢的一次(单位ms) |
| Un-Health services(Apdex) | 每个服务健康指数:0-1(1为非常健康) |
| Slow Endpoints | 所有服务慢请求排行榜 |
| Global Response Latency | 百分比的响应时间(单位ms) |
| Global Heatmap | 服务响应时间热力分布图(根据响应时间的数量显示颜色深度) |
Service服务维度
| 图表 | 描述 |
|---|---|
| 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实例维度
| 图表 | 描述 |
|---|---|
| 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 Time | jvm垃圾回收时间(YGC和OGC) |
| JVM GC Count | jvm垃圾回收次数(YGC和OGC) |
| JVM Thread Count (Java Service) | jvm创建线程数量 |
Endpoint端点维度
| 图表 | 描述 |
|---|---|
| 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
| 图表 | 描述 |
|---|---|
| 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) | 所有数据库健康排名、请求成功率排名 |
性能剖析
性能监测分析:根据服务、端点、规则建立性能监测任务后会自动记录并生成剖析结果
主要用于慢响应请求排查
-
新建监控任务
-
浏览器发起请求
-
查看采样结果
-
性能分析
日志
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>
-
请求接口看控制台日志
可以发现请求中带有TID(traceID)
-
查看skywalking日志
告警
告警功能其核心由一组规则驱动而来,这些规则定义在config/alarm-settings.yml文件中
主要分为告警规则和网络钩子两部分
告警规则
告警规则是定义什么条件下触发度量警报,默认在config/alarm-settings.yml文件中定义了一些常用的告警规则
过去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类
[{ "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 | 服务名 |
| id0 | scope实体的 ID |
| id1 | 保留字段,目前暂未使用 |
| ruleName | 告警规则名称 |
| alarmMessage | 告警消息内容 |
| startTime | 告警时间,格式为时间戳 |
-
编写回调接口
-
配置回调地址