Apache SkyWalking
1. 介绍
SkyWalking:一个APM(应用程序性能监视器)系统,专门为微服务,云原生和基于容器(Docker,Kubernetes,Mesos)的体系结构而设计。
SkyWalking是一个开源APM系统,包括对Cloud Native体系结构中的分布式系统的监视,跟踪,诊断功能。核心功能如下:
- 服务,服务实例,端点指标分析
- 根本原因分析。在运行时分析代码。阅读Apache SkyWalking:使用分析来修复分布式跟踪的盲点。
- 服务拓扑图分析
- 服务,服务实例和端点依赖关系分析
- 检测到慢速服务和端点
- 性能优化
- 分布式跟踪和上下文传播
- 数据库访问指标。检测慢速数据库访问语句(包括SQL语句)。
- 报警
1.1 SkyWalking是什么
skywalking是一个国产开源框架,2015年由吴晟开源,2017年加入Apache孵化器。skywalking是分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。它是一款优秀的APM(Application Perfomance Management)工具,包括了分布式追踪、性能指标分析、应用和服务依赖分析等。
1.2 链路追踪框架对比
- Zipkin是Twitter开源的调用链分析工具,目前基于springcloud sleuth得到了广泛的使用,特点是轻量,使用部署简单。
- Pinpoit是韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能强大,接入端无代码侵入。
- skywalking是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,U功能较强,接入端无代码侵入。目前已加入Apache孵化器。
- CAT是大众点评开源的基于编码和配置的调用链分析,应用监控分析,日志采集,监控报警等一系列的监控平台工具。
1.3 Skywalking主要功能特性
- 多种监控手段,可以通过语言探针和service mesh获得监控的数据;
- 支持多种语言自动探针,包括Java,.NET Core和Node.JS;
- 轻量高效,无需大数据平台和大量的服务器资源;
- 模块化,UI、存储、集群管理都有多种机制可选;
- 支持告警;
- 优秀的可视化解决方案;
2 环境部署
- skywalking agent和业务系统绑定在一起,负责收集各种监控数据
- Skywalking oapservice是负责处理监控数据的,比如接受skywalking agent的监控数据,并存储在数据库中;接受skywalking webapp的前端请求,从数据库查询数据,并返回数据给前端。Skywalking oapservice通常以集群的形式存在
- skywalking webapp,前端界面,用于展示数据
- 用于存储监控数据的数据库,比如mysql、elasticsearch等。
2.1 下载
- 下载
- 安装的时候一定要注意安装文件夹不能有空格,否则不能启动。
- 若出现启动错误可以查看日志分析:logs/skywalking-oap-server
2.2 安装文件目录结果
目录结构
- web app: Ul前端(web 监控页面)的jar包和配置文件;
- oap-libs:后台应用的jar包,以及它的依赖jar包,里边有一个server-starter-*.jar就是启动程序;
- config:启动后台应用程序的配置文件,是使用的各种配置
- bin:各种启动脚本,一般使用脚本startup.*来启动web页面和对应的后台应用;
- oapService.:默认使用的后台程序的启动脚本;(使用的是默认模式启动,还支持其他模式,各模式区别见启动模式). oapServicelnit.:使用init模式启动;在此模式下,OAP服务器启动以执行初始化工作,
然后退出 - oapServiceNoInit.*:使用no init模式启动;在此模式下,OAP服务器不进行初始化。
- webappService.*: UI前端的启动脚本;
- startup.*:组合脚本,同时启动oapService.*脚本;webappService.*脚本;
- oapService.:默认使用的后台程序的启动脚本;(使用的是默认模式启动,还支持其他模式,各模式区别见启动模式). oapServicelnit.:使用init模式启动;在此模式下,OAP服务器启动以执行初始化工作,
- agent:
- skywalking-agent.jar:代理服务jar包. config:代理服务启动时使用的配置文件
- plugins:包含多个插件,代理服务启动时会加载改目录下的所有插件(实际是各种jar包)
- optional-plugins:可选插件,当需要支持某种功能时,比如SpringCloud Gateway,则需要把对应的jar包拷贝到plugins目录下(默认不包含Gateway);
2.3 搭建Skywalking OAP服务
- 启动脚本bin/startup.bat
- 日志存储在logs目录
- 启动成功后会启动两个服务,一个是skywalking-oap-server,一个是skywalking-web-ui : localhost:8080
skywalking-oap-server服务启动后会暴露11800和12800两个端口,分别为收集监控数据的端口 11800和接受前端请求的端口12800。 - 修改端口可以修改config/applicaiton.yml
- skywalking-web-ui服务会占用8080端口,修改端口可以修改webapp/webapp.yml
- server.port: SkyWalking UI服务端口,默认是8080;
- collector.ribbon.listOfServers:SkyWalking OAP服务地址数组,SkyWalking UI界面的数据是通过请求SkyWalking OAP服务来获得;
2.4 SkyWalking中三个概念
- 服务(Service): 表示对请求提供相同行为的一系列或一组工作负载,在使用Agent时,可以定义服务的名字;
- 服务实例(Service Instance): 上述的一组工作负载中的每一个工作负载称为一个实例,一个服务实例实际就是操作系统上的一个真实进程;
- 端点(Endpoint): 对于特定服务所接收的请求路径,如HTTP的URI路径和gRPC服务的类名+方法签名;
3.SkyWalking接入微服务
3.1 linux环境—通过jar包方式接入
- 准备一个spingboot程序,打成可执行jar包,写一个shell脚本,在启动项目的Shel脚本上,通过-javaagent参数进行配置SkyWalking Agent来跟踪微服务;
- startup.sh脚本:
# Skywalking Agent配置
export SW_AGENT_NAME=springboot-skywalking-demo #Agent名字,一般使用`spring.application.name'
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 #配置collector地址。
export SW_AGENT_SPAN_LIMIT=2000 #配置链路的最大span数量,默认为300。
export JAVA_AGENT=-javaagent:/usr/local/soft/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
java $JAVA_AGENT -jar springboot-skywalking-demo-0.0.1-SNAPSHOT.jar #jar启动
3.2 windows环境—在IDEA中使用Skywalking
在运行的程序配置jvm参数,如下图所示:
# skywalking-agent.jar的本地磁盘的路径
-javaagent:D:apache\apache-skywalking-apm-es7-8.4.0\apache-skywalking-apm-bin-es7\agent\skywalking-agent.jar
#在skywalking上显示的服务名
-DSW_AGENT_NAME=tps_service
# skywalking的collector服务的IP及端口
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
注意: 跟踪链路不显示gateway
- 拷贝agent/optional-plugins目录下的gateway插件到agent/plugins目录
3.3 Skywalking跨多个微服务跟踪
- Skywalking跨多个微服务跟踪,只需要每个微服务启动时添加javaagent参数即可。
- 测试:
启动微服务mall-gateway,mall-order,mall-user,配置skywalking的jvm参数http://localhost:8888/user/findOrderByUserld/1
4. Skywalking持久化跟踪数据
默认使用的H2数据库存储,configlapplication.yml
4.1基于mysql持久化:
1.修改config目录下的application.yml,使用mysql作为持久化存储的仓库
2.修改mysql连接配置
3. 注意oap-libs内没有MySql驱动
需要自行复制MySql驱动jar包到该目录。
5 自定义SkyWalking链路追踪
如果我们希望对项目中的业务方法,实现链路追踪,方便我们排查问题,可以使用如下的代码引入依赖:
< ! -- Skywalking工具类,和服务版本一致-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.5.0</version>
</dependency>
5.1 @Trace将方法加入追踪链路
如果一个业务方法想在ui界面的跟踪链路上显示出来,只需要在业务方法上加上@Trace注解即可
5.2加入@Tags或@Tag
我们还可以为追踪链路增加其他额外的信息,比如记录参数和返回信息。实现方式:在方法上增加@Tag或者@Tags。
@Tag注解中key =方法名,value = returnedObj表示返回值,arg[0]表示参数
6 性能分析
skywalking的性能分析,在根据服务名称、端点名称,以及相应的规则建立了任务列表后,在调用了此任务列表的端点后。sky walking会自动记录,剖析当前端口,生成剖析结果。
7 Skywalking集成日志框架
1.logback官方配置
2.log4j官方配置
3.log4j2j官方配置引入依赖
这里主要介绍logback的使用
- 引入依赖
<!--apm-toolkit-logback-1.x -->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>8.5.0</version>
</dependency>
- 引入logback配置文件,并加入[%tid]来记录链路id
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<property name="log.path" value="C:/document/javaCode/log" />
<!-- 彩色日志 -->
<!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 -->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss})[%tid] |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<!-- 例如:如果此处配置了INFO级别,则后面其他位置即使配置了DEBUG级别的日志,也不会被输出 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 WARN 日志 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</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>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。
<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
如果未设置此属性,那么当前logger将会继承上级的级别。
-->
<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="INFO">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别:
-->
<!--开发环境:打印控制台-->
<springProfile name="dev">
<!--可以输出项目中的debug日志,包括mybatis的sql日志-->
<logger name="com.guli" level="INFO" />
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG
可以包含零个或多个appender元素。
-->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</springProfile>
<!--生产环境:输出到文件-->
<springProfile name="pro">
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="grpc-log" />
</root>
</springProfile>
</configuration>
- 引入gRPC reporter
<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>
- 配置skyworking,若Skyworking不在本地
在agient/config进行配置
8 Skywalking告警功能
8.1 告警功能简介
- Skywalking每隔一段时间根据收集到的链路追踪的数据和配置的告警规则(如服务响应时间、服务响应 时间百分比)等,判断如果达到阈值则发送相应的告警信息。
- 发送告警信息是通过调用webhook接口完成,具体的webhook接口可以使用者自行定义,从而开发者可以在指定的webhook接口中编写各种告 警方式,比如邮件、短信等。
- 告警的信息也可以在RocketBot中查看到。
文件定义了默认的4种告警规则
- 最近3分钟内服务的平均响应时间超过1秒
- 最近2分钟服务成功率低于80%
- 最近3分钟90%服务响应时间超过1秒
- 最近2分钟内服务实例的平均响应时间超过1秒 规则中的参数属性如下
以下是默认的告警规则配置,位于skywalking安装目录下的config文件夹下 alarm-settings.yml文件 中:
rules:
# Rule unique name, must be ended with `_rule`.
service_resp_time_rule:
metrics-name: service_resp_time #oal脚本中的度量名称
op: ">" # 比较操作符,可以设定>,<,=
threshold: 1000 #阈值,与metrics-name和比较符号相匹配
period: 10 #多久检查一次当前的指标数据是否符合告警规则,单位分钟
count: 3 #达到多少次后,发送告警消息
silence-period: 5 #在多久之内,忽略相同的告警消息
message: Response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes. #告警消息内容
service_sla_rule:
# Metrics value need to be long, double or int
metrics-name: service_sla
op: "<"
threshold: 8000
# The length of time to evaluate the metrics
period: 10
# How many times after the metrics match the condition, will trigger alarm
count: 2
# How many times of checks, the alarm keeps silence after alarm triggered, default as same as period.
silence-period: 3
message: Successful rate of service {name} is lower than 80% in 2 minutes of last 10 minutes
service_resp_time_percentile_rule:
# Metrics value need to be long, double or int
metrics-name: service_percentile
op: ">"
threshold: 1000,1000,1000,1000,1000
period: 10
count: 3
silence-period: 5
message: Percentile response time of service {name} alarm in 3 minutes of last 10 minutes, due to more than one condition of p50 > 1000, p75 > 1000, p90 > 1000, p95 > 1000, p99 > 1000
service_instance_resp_time_rule:
metrics-name: service_instance_resp_time
op: ">"
threshold: 1000
period: 10
count: 2
silence-period: 5
message: Response time of service instance {name} is more than 1000ms in 2 minutes of last 10 minutes
database_access_resp_time_rule:
metrics-name: database_access_resp_time
threshold: 1000
op: ">"
period: 10
count: 2
message: Response time of database access {name} is more than 1000ms in 2 minutes of last 10 minutes
endpoint_relation_resp_time_rule:
metrics-name: endpoint_relation_resp_time
threshold: 1000
op: ">"
period: 10
count: 2
message: Response time of endpoint relation {name} is more than 1000ms in 2 minutes of last 10 minutes
# Active endpoint related metrics alarm will cost more memory than service and service instance metrics alarm.
# Because the number of endpoint is much more than service and instance.
#
# endpoint_avg_rule:
# metrics-name: endpoint_avg
# op: ">"
# threshold: 1000
# period: 10
# count: 2
# silence-period: 5
# message: Response time of endpoint {name} is more than 1000ms in 2 minutes of last 10 minutes
webhooks:
# - http://127.0.0.1/notify/
# - http://127.0.0.1/go-wechat/
9 Skywalking高可用
在大多数生产环境中,后端应用需要支持高吞吐量并且支持高可用来保证服务的稳定,所以你始终需要在生产环境进行集群管理。
Skywalking集群是将skywalking oap作为一个服务注册到nacos上,只要skywalking oap服务没有全部宕机,保证有一个skywalking oap在运行,就能进行跟踪。
- 搭建一个skywalking oap集群需要:
(1)至少一个Nacos (也可以是nacos集群)
(2)至少一个ElasticSearch/mysql(也可以是es/msql集群)
(3)至少2个skywalking oap服务;
(4)至少1个UI(UI也可以集群多个,用Nginx代理统一入口)