SkyWalking
Skywalking简介
SkyWalking是一个开源的可观察性平台,用于收集,分析,聚合和可视化来自本地或者云服务中的数据。即使在整个云环境中,SkyWalking也能提供一种简便的方法来维护您的分布式系统的清晰视图。它是一个现代的APM(Application Performance Monitor 应用性能监测软件),专门为基于云、容器的分布式系统而设计。
为什么要选择skywalking?
SkyWalking提供了用于在许多不同情况下观察和监视分布式系统的解决方案,并通过agent方式,做到高性能、低损耗、无侵入性,与类似的功能组件如:Zipkin、Pinpoint、CAT相比,skywalking无论是从性能还是社区活跃度方面考虑,都具有一定的优势。
skywalking监控维度
skywalking从三个维度提供可观察项功能,分别是:服务,服务实例,端点
服务。表示一组/一组工作负载,这些工作负载为传入请求提供相同的行为。
服务实例。服务组中的每个单独工作负载都称为实例。像pods在Kubernetes中一样,它不必是单个OS进程,但是,如果您使用agent代理,则实例实际上是一个真正的OS进程。
端点。服务中用于传入请求的路径,例如HTTP URI路径或gRPC服务类+方法签名。
skywalking架构
从逻辑上看,skywalking分为四个部分:探针,平台后端,存储和UI。
探针:收集数据并重新格式化以符合SkyWalking的要求(不同的探针支持不同的来源)。
平台后端:支持数据聚合,分析和流处理,涵盖跟踪,指标和日志。
存储:设备通过开放/可插入的界面存储SkyWalking数据。您可以选择现有的实现,例如ElasticSearch,H2,MySQL,TiDB,InfluxDB,或者实现自己的实现。
UI:是一个高度可定制的基于Web的界面,允许SkyWalking最终用户可视化和管理SkyWalking数据。
skywalking安装
直接进入官方下载:https://skywalking.apache.org/downloads/ 下载之后上传到服务器并安装
agent目录:将来要拷贝到各服务所在机器上用作探针
bin目录:服务启动脚本
config:配置文件
oap-libs:oap服务运行所需要的jar包(oap服务就是skywalking的后端服务Observability Analysis Platform)
webapp:web服务运行所需要的jar包
启动
cd到bin目录
执行./startup.sh直接同时启动 oap和web服务
访问
访问你的服务器地址,8080端口,就可以进入skywalking的UI界面
替换存储源
默认情况下skywalking使用H2来存储数据,这肯定无法满足性能要求,所以我们使用ES来进行存储。
elasticsearch安装
下载
下载elasticsearch,本教程选择6.4.0版本
解压、启动
同样直接解压,并进入elasticsearch-6.4.0目录
进入bin目录,启动./elasticserach,你可能会遇到如下报错,表明不能使用root用户启动es
org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root
at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:140) ~[elasticsearch-6.4.0.jar:6.4.0]
at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:127) ~[elasticsearch-6.4.0.jar:6.4.0]
at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) ~[elasticsearch-6.4.0.jar:6.4.0]
at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124) ~[elasticsearch-cli-6.4.0.jar:6.4.0]
at org.elasticsearch.cli.Command.main(Command.java:90) ~[elasticsearch-cli-6.4.0.jar:6.4.0]
at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:93) ~[elasticsearch-6.4.0.jar:6.4.0]
at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:86) ~[elasticsearch-6.4.0.jar:6.4.0]
Caused by: java.lang.RuntimeException: can not run elasticsearch as root
at org.elasticsearch.bootstrap.Bootstrap.initializeNatives(Bootstrap.java:104) ~[elasticsearch-6.4.0.jar:6.4.0]
at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:171) ~[elasticsearch-6.4.0.jar:6.4.0]
at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:326) ~[elasticsearch-6.4.0.jar:6.4.0]
at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:136) ~[elasticsearch-6.4.0.jar:6.4.0]
… 6 more
所以我们需要重新建一个用户,并用新用户启动es
比如我重新建立了一个用户就叫es
启动
启动成功后,我们通过curl http://localhost:9200测试,看到如下内容就表示es启动成功。
你也可以使用如下命令,用后台方式启动。
./elasticsearch -d
配置外部访问
cd到elasticsearch的config目录中
修改elasticsearch.yml文件,找到network.host配置项,默认是注释着的,打开注释,并修改为0.0.0.0,保存退出,重启es服务即可。
vi elasticsearch.yml
那么现在就可以通过外部访问了
替换skywalking存储方式为es
现在es服务已经配置完成了,我们cd到skywalking的 config目录下
修改application.yml文件
找到存储源的配置,直接把h2改成es即可
默认配置:
换成:elasticsearch
保存并启动skywalking
看到日志中有创建一些es索引的过程,表示已经成功使用es作为存储源了
配置agent
现在skywalking还无法监控服务,需要我们配置agent,skywalking提供的agent本身就是一个jar包,所以启动时直接新增javaagent配置项,指定好skywalking-agent.jar的所在的路径即可。
idea集成
为了演示方便,直接使用idea配置启动。
首先配置javaagent:指定agent目录下的skywalking-agent.jar所在的路径
service_name名称根据自己的情况定义
然后注意backend_service,端口为11800 grpc访问的端口
-javaagent:D:/learn/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar
-Dskywalking.agent.service_name=order
-Dskywalking.collector.backend_service=10.0.0.189:11800
配置完启动服务,此时再访问就可以看到你的服务了
接口测试一下
我们可以看到,本身Rocketbot UI,也为我们从4个角度进行了监控,全局的,服务、实例、端点,一开始也提到过后面3个本身也是skywalking的监控维度
那我们再启动一个微服务,并且通过feign的方式,进行微服务之间的调用,那我们可以看到结果如下,skywalking可以为我们监控到每一次请求所执行的路径,已经每个路径上的耗时时间。
总结
skywalking拥有完整的APM和调用链路功能,扩展性强、二次开发简单,使用agent采集数据,并经过collector简单处理数据、aggregator聚合数据统计分析并持久化、alarm监控告警,最终提供query查询,和UI呈现。
自定义链路追踪:
例如希望追踪某个业务方法的入参及返回值(点击链路即可看到信息)
第一步:需要引入依赖
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<!-- 和sw的版本一致即可 -->
<version>8.5.0</version>
</dependency>
第二步:在业务方法上面加上@Trace注解 (该注解为记录方法注解)
第三步:再追加上@Tags或@Tag注解 (该注解用于记录方法的入参或返回值)
key一般可以指定为方法名或参数名(自定义),
value不能随便填 arg[0]表示下标为0的参数 ,returnedObj表示返回值
@Trace
@Tags({@Tag(key = "param", value = "arg[0]"), @Tag(key = "test", value = "returnedObj")})
private String test(String param) {
return "xxx";
}
性能剖析:
可以定位到具体代码 占用时间
操作方法:性能剖析页面点击新建任务
选择实例服务 输入端点名称(接口相对路径url)
skywalking链路追踪与logback日志整合
需要在oap-libs 目录中加上 log4j-web.jar
(忘记了自己最早为什么写的需要这个包,
后面经过测试可以不加 但基于对自己的信任 这句话先留着 万一就踩坑了呢)
第一步: 在pom文件中添加整合依赖
<!-- skywalking和logback整合 print traceId in our logs-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
</dependency>
第二步: 在resources下新建名为logback-spring.xml 的文件
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!-- 引入springboot 默认的 logback xml 配置文件 (springboot default log config)-->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志的格式化-->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<!-- 来源于defaults.xml 可以全局查找文件找到springboot中的该配置文件 如果无需更改 只需要引入>${CONSOLE_LOG_PATTERN}-->
<!-- 我们需要记录traceId 所以需要加上[%tid] 加在下面任意一个不破坏格式的地方-->
<!--source from springboot logback defaults.xml,default:${CONSOLE_LOG_PATTERN}
but we need to note the traceId ,add [%tid]-->
<!-- https://github.com/apache/skywalking/tree/master/docs/en/setup/service-agent/java-agent -->
<Pattern>-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint}%clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} [%tid] %clr(---){faint}%clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint}%m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}</Pattern>
</layout>
</encoder>
</appender>
<!-- grpc方式将traceId推送至skywalking-->
<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>
<!--设置appender -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="grpc-log"/>
</root>
</configuration>
注意:当skyWalking部署在远程服务的时候 需要在./agent/config目录下面对agent.config配置文件追加:
SW_GRPC_LOG_SERVER_HOST 对应服务地址 没有该配置默认为127.0.0.1
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:192.168.0.1}
plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}
这里有个坑,如果配置反复检查没有配错,skywalking的控制台仍看不到日志信息(如果能看到直接跳过),那看看是不是和我一样踩坑了:
因为我是在个人项目中搭建的,个人项目之前为了模拟传说中的log4j史诗级bug, 需要把logback等日志的实现给排除 (log4j其实只是接口 真正的bug位于log4j-core包),所以我把springboot中 最常用的这个spring-boot-starter-logging包给排除了,等到需要skywalking和logback整合的时候 又单独引用了slf4j-api,logback-core,logback-classic 三个依赖,结果发现一直显示不了日志。 最后想来想去 应该是日志依赖这边出了问题,删除单独引入的依赖,直接引入starter-logging依赖,成功解决问题。
<!-- skyWalking与logback整合 需要的logback依赖
且已测试只加入slf4j-api logback-core与logback-classic这三个包无效 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<!-- 测试log4j bug时 需要排除starter-logging包下所有依赖 -->
<!-- <exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>-->
</dependency>
告警功能
SkyWalking告警功能是在6.x版本新增的的,其核心由一组规则驱动,这些规则定义在config目录的alarm-settings.yml文件中。
SkyWalking的发行版都会默认提供该配置,里面预先定义了一些常用的告警规则
1. 过去3分钟内服务平均响应时间超过1秒
2.过去2分钟服务成功率低于80%
3.过去3分钟内服务响应时间超过1s的百分比
4,服务实例在过去2分钟内平均响应时间超过1s.并且实例名称与正则表达式匹配,
5.过去2分钟内端点平均响应时间超过1秒
6.过去2分钟内数据库访问平均炯应时间超过1秒
7.过去2分钟内端点关系平均响应时间超过1秒。
告警规则配置项的说明
规则名称: 告警信息中显示的唯一名称。必须以过 _rule 结尾,前綴可自定义
Metrics name:配置时只需要取 _rule 前的名称;度量名称,取值为oal脚本中的度量,目前只支持long、double和Int型
Include names:该规则作用于哪些实体名称,比如服务名,终端名(可选,默认为全部〕
Exclude names:该规则作不用于哪些实体名称,比如服务名,终惴名(可选,默认为空)
Threshold:阈值, 例如2000为2s
OP:操作符,目前支持> < =
Period:多久告警规则需要被核实一下。这是一个时间窗囗,与后端部署环境时间相匹配
Count: 在一个Period窗口中,如值values超过Threshold值(按op) 达到count值需要发送警报
Silence period : 在时间N中 触发报警后,在TN->TN+ period 这个阶段不告警 ,默认情况下 它和period一样,意味着相同的告警 在同一个period内只会触发一次
webhooks: 网络钩子 可以理解为web回调 出现告警时,可以往我们任意一个服务发送请求,
我们可以在服务里面写业务代码:如发送短信、邮件等 请求方式:http, post, Content-type:application/json
基于List<org.apache.skywalking.oap.server.core.alarm.AlarmMessage>进行序列化的
,可以自定义一个类 字段保持上述一致
配置示例
rules:
# Rule unique name, must be ended with `_rule`.
service_resp_time_rule:
metrics-name: service_resp_time
op: ">"
threshold: 1000
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
exclude-labels:
# 过滤状态码 200 401 404 注意前面有个小横杠
- "200,401,404"
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
告警面板
skywalking的告警页面,可以看到我们的告警信息。如果之前的告警 页面突然看不到了,看看是不是时间筛选的问题,默认的时间跨度不会很长,不得不吐槽其它页面上方都有时间筛选 而告警页面只有右下角才有,每次搭建后都以为是出现了bug。
告警通知
上面介绍的是告警功能,只能在skywalking的控制台面板上看到,那如果我们想要通知功能该怎么办呢? 同样是在config目录的alarm-settings.yml配置中
找到webhooks:节点
注意前面有个 横杠 - ,建议打开注释并修改默认地址 避免出错,指定接口后,skywalking告警消息在短暂的延迟后会调用我们的接口进行通知, 我们可以在接口里面写发送邮件、发送短信 、通知到钉钉群等代码功能,取决于我们需求。
接下来演示告警信息发送邮件
1、配置回调接口
在 config/alarm-settings.yml中配置回调接口(我们应用服务的接口),并重启 skywalking服务。
webhooks:
# - http://127.0.0.1/notify/
# - http://127.0.0.1/go-wechat/
- http://127.0.0.1:18081/sw/alarm/notify/
- http://127.0.0.1:18081/sw/alarm/notify2/
2、编写回调接口
在我们应用服务中编写接口。拿到告警信息,通过不同的方式通知工作人员。
2.1 控制台输出告警信息
简单点,控制台输出告警信息。
@RestController
@RequestMapping("/sw/alarm")
public class SkywalkingAlarmController {
/**
* Skywalking告警通知回调接口:http://127.0.0.1:18081/sw/alarm/notify/
*
* @param obj
* @return
*/
@RequestMapping("/notify")
public String notify(@RequestBody Object obj) {
// TODO 将告警信息通知给负责人。比如:通过发短信,钉钉消息,邮件,微信通知等方式发送给技术负责人
System.err.println("收到Skywalking告警信息:" + obj.toString());
return "notify successfully";
}
}
2.2 发送邮件
SpringBoot 实现发送邮件:https://blog.csdn.net/qq_42402854/article/details/110472398
/**
* 发送邮件
*
* Skywalking告警通知回调接口:http://127.0.0.1:18081/sw/alarm/notify2/
*
* @param obj
* @return
*/
@RequestMapping("/notify2")
public String notify2(@RequestBody Object obj) {
// TODO 将告警信息通知给负责人。比如:通过发短信,钉钉消息,邮件,微信通知等方式发送给技术负责人
System.err.println("notify2 收到Skywalking告警信息:" + obj.toString());
sendSimpleMail("xxx@qq.com", "Skywalking告警信息", obj.toString());
return "notify successfully";
}
private static final String SENDER = "xxx@163.com";
@Autowired
private JavaMailSender mailSender;
/**
* 发送普通邮件
*
* @param to 收件人邮箱
* @param subject 主题(标题)
* @param content 内容
*/
public void sendSimpleMail(String to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(SENDER);
message.setTo(to);
message.setSubject(subject);
message.setText(content);
try {
mailSender.send(message);
} catch (Exception e) {
System.out.println("发送普通邮件时发生异常!" + e);
}
}
ired
private JavaMailSender mailSender;
/**
* 发送普通邮件
*
* @param to 收件人邮箱
* @param subject 主题(标题)
* @param content 内容
*/
public void sendSimpleMail(String to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(SENDER);
message.setTo(to);
message.setSubject(subject);
message.setText(content);
try {
mailSender.send(message);
} catch (Exception e) {
System.out.println("发送普通邮件时发生异常!" + e);
}
}