目录
1.什么是SkyWalking
2.SkyWalking环境搭建部署
2.1下载skywalking
2.2启动服务
2.3访问前端ui页面
3.Skywalking接入微服务
3.1linux环境--通过jar包方式接入
3.2windos环境--在IDEA中配置SkyWalking
3.3wkywalking监控界面
3.4接入多个微服务
4.Skywalking持久化跟踪数据
4.1基于mysql的数据持久化
5.自定义skywalking链路追踪
6.性能剖析
7.Skywalking集成日志框架
8.skywalking告警
8.1告警规则
8.2webhook(网络钩子)
wkywalking是一个国产开源框架,由吴晟开源,2017年加入Apache孵化器。skywalking是分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)框架而设计。它是一款优秀的APM(Application Performance Management)工具,包括了分布式追踪、性能指标分析、应用和服务依赖分析等。
官网:Apache SkyWalking
下载:Downloads | Apache SkyWalking
文档:https://skywalking.apache.org/docs/main/v8.7.0/readme/
中文文档:SkyWalking 文档中文版(社区提供)
wkywaling主要功能特性:
(1)多种监控手段,可以通过语言探针和service mesh获得监控的数据
(2)支持多种语言自动探针,包括Java,.net Core 和Node.js
(3)轻量高效,无需大数据平台和大量的服务器资源
(4)模块化、UI、存储、集群管理都有多种机制可选
(5)支持告警
(6)优秀的可视化解决方案
.skywalking agent和业务系统绑定在一起,负责收集各种监控数据
.sykwalking oapservice是负责处理监控数据的,比如接收skywalking agent的监控数据,并存储在数据库中;接收skywalking webapp的前端请求,从数据库查询数据,并返回数据给前端。skywalking oapservice通常以集群的形式存在。
.skywalking webapp,前端界面,用于展示数据。
.数据库用于存储监控数据,比如mysql、elasticsearch等。
地址:Downloads | Apache SkyWalking
下载的版本提供elasticSearch和mysql等的版本,我们使用mysql版本的。下载tar压缩包,解压后目录:
目录结构:
①webapp:UI前端(web监控页面)的jar包和配置文件;
ui页面的启动端口,可以修改webapp.yml,默认是8080
②oap-libs:后台应用的jar包,以及它依赖的jar包,里边有一个server-starter-*.jar就是启动程序;
③config:启动后台应用程序的配置文件,是使用的各种配置
在application.yml中可以配置使用的存储方式等信息,默认使用h2内存方式
④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,则需要把对应的jar包拷贝到plugins目录下
双击bin/startup.bat启动服务,同时会启动oapservice和webapp两个服务
控制台没有答应启动是否成功以及端口等信息,当启动时会创建logs文件夹,分别生成skywalking-oap-server.log、webapp.log
skywalking-oap-server会暴露11800和12800两个端口,分别为收集监控数据的端口11800和接受前端ui请求的端口12800,修改端口可以修改config/application.yml
根据webapp/webapp.yml中配置的ui的启动端口,访问ui页面
准备一个springboot程序,打成可执行jar包,写一个shell脚本,在启动项目的Shell脚本上,通过-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启动
在运行的程序配置jvm参数,如下图所示:
-javaagent:D:\software\apache-skywalking-apm-bin-es7\agent\skywalking-agent.jar
-DSW_AGENT_NAME=api-service
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
启动系统,访问接口,此时查看skywalking监控页面,没有任何数据,当前服务下拉有配置的api-service服务。
没有显示任何链路追踪信息的原因:agent/plugins目录中缺少gateway的jar包。
从agent/optional-plugins(扩展插件)中复制 gateway的jar包到agent/plugins目录中。
重新启动skywalking服务,以及springboot项目服务,在访问接口服务,skywalking控制台有检测数据。
仪表盘:显示服务、端点、慢服务等
拓扑图(调用关系),user访问api-service,api-service调用的ip和端口,由于8084的服务还没有加入skywalking,所以此处只显示ip和端口
追踪:调用服务的链路追踪,以及消耗的时间长短
为了访问数据库,order的服务我们访问seate的项目,在OrderSeataApplication启动类的VM options中添加wkywalking的配置:
在StockSeateApplication启动类的VM options中添加:
先启动seate、nacos服务,再启动这三个项目
此时nacos中是可以看到这几个服务的
访问接口服务,此时wkywalking控制台可以看到多个服务,多个调用的端点
拓扑图显示了从user开始调用api-service网关服务,网关服务调用order-seata,order-seate调用数据库以及调用stock-seata;stock-seata调用数据库
追踪:显示了服务调用的完整过程,以及花费的时间
默认使用H2(内存)数据库存储,在config/application.yml中配置,重启服务后数据会丢失。
(1)修改config/application.yml中的数据源为mysql
(2)修改config/application.yml中的mysql连接配置
(3)mysql中创建数据库,名字为(2)中配置的数据库名
(4)重新启动startup.bat,发现oap-service服务启动不成功,查看skywalking-oap-server.log日志,显示获取不到数据库驱动。
(5)从我们本地maven私服中复制一份mysql驱动,粘贴到wkywalking启动的oap-libs中
(6)启动程序,发现oap-service还是启动失败
需要在连接mysql数据库连接中添加时区serverTimezone=GMT
(7)启动startup.bat,启动程序,skywalking会默认往我们配置的swtest数据库中添加需要的表
(8)访问接口,skywalking控制台有记录
重新启动skywalking后,直接刷新控制台页面,之前的数据还是存在的
如果我们希望对项目中的业务方法,实现链路追踪,方便我们排查问题,可以使用如下的方式:
(1)pom.xml中引入依赖
org.apache.skywalking
apm-toolkit-trace
8.5.0
(2)调用链路要显示具体的方法,在调用的方法中添加注解@Trace
(3)wkywalking控制台调用链路显示类名到接口名的方法
(4)调用链路中显示返回值,以及参数值信息,使用@Tag或者@Tags
@Tag注解中key=方法名 value=returnedObj 返回值 agr[0] 参数
param标识参数,returnedObj标识返回值
@RequestMapping("/add")
@Trace
@Tag(key = "add",value ="returnedObj")
public String add() {
System.out.println("下单成功");
// String forObject = restTemplate.getForObject("http://localhost:8083/stock/reduct", String.class);
String forObject = restTemplate.getForObject("http://stock-service/stock/reduct", String.class);
//int a= 1/0;
return "add order " + forObject;
}
@RequestMapping("/getById/{id}")
@Trace
@Tags({@Tag(key = "getById",value ="returnedObj"),
@Tag(key="param",value = "arg[0]")
})
public String getById(@PathVariable("id") Integer id) {
return "根据id获取信息";
}
(5)访问接口后,在追踪中点击方法名
记录了返回值的key为add,以及返回的value值
还可以记录参数
skywalking的性能剖析,在根据服务名称、端点名称、以及相应的规则建立了任务列表后,在调用了此任务列表的端点后,skywalking会自动记录,剖析当前端口,生成剖析结果,具体流程如图:
(1)一开始性能剖析菜单是没有内容的,需要添加剖析的接口;点击新建任务创建
(2)sleep接口中睡眠2秒,访问接口,查看skywalking控制台的性能剖析
选中接口/order/sleep,点击分析展开调用链路
调用链路展开后可以看到花费时间最久的代码
springboot默认的日志配置:logback-spring.xml
(1)在订单服务的pom.xml中引入依赖
org.apache.skywalking
apm-toolkit-logback-1.x
8.5.0
(2)在订单服务的resources目录中添加logback-spring.xml配置文件,日志级别使用INFO。
${CONSOLE_LOG_PATTERN}
(3)logback-spring.xml中的
%d{-yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36}
程序启动时开始打应trace的id,由于没有访问过接口,所以为N/A
(4)当访问接口时,控制台会打印输出TID信息
(5)复制打应的tid可以去skywalking控制台中根据追踪id搜素记录
使用gRPC报告程序可以将收集到的日志转发到skywalking oap服务器上。
①在logback-spring.xml中添加grpc的配置
%d{-yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n
logback-spring.xml完整配置:
%d{-yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36}
%d{-yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n
②访问接口服务,查看skywalking控制台,此时日志页面出现访问的日志信息,包括程序启动的控制台日志信息,可以点击追踪id查看链路访问情况
.③若skywalking服务不是部署在本地机器上,则grpc上报需要做而外的配置才能把日志上报上去。
在agent/config目录下有agent.config配置文件,添加日志的grpc配置信息
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:127.0.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告警功能在6.x版本中新增的,其核心由一组规则驱动,这些规则定义在config/alarm-settings.yml文件中。告警规则的定义分为两部分:
(1)告警规则:它们定义了应该如何触发度量警报,应该考虑什么条件。
(2)Webhook(网络钩子):定义当警告触发时,哪些服务终端需要被告知。
skywalking的发行版会默认提供config/alarm-setting.yml文件,里面预先定义了一些常用的告警规则,如下:
①过去3分钟内服务平均响应时间超过1秒
②过去2分钟服务成功率低于80%
③过去3分钟内服务响应时间超过1s的百分比
④服务实例在过去2分钟内平均响应时间超过1s,并且实例名称和正则表示式匹配。
⑤过去2分钟内端点平均响应时间超过1秒。
⑥过去2分钟内数据库访问平均响应时间超过1秒
⑦过去2分钟内端点关系平均响应时间超过1秒
这些预定义的告警规则,打开config/alarm-setting.yml文件即可看到
告警规则配置说明:
.Rule name:规则名称,也是在告警信息中显示的唯一名称,必须以_rule结尾,前缀自定义
.Metrics name:度量名称
.Include names:该规则作用于哪些实体类名称,比如服务名、终端名(可选,默认全部)
.Exclude names:该规则不用于哪些实体名称,比如服务名、终端名(可选,默认为空)
.Threshold:阈值
.OP:操作符,目前支持>、<、=
.Period:多久告警规则需要被核实一下,
.Count:在一个Period窗口中,如果values超过Threshold值(按op),达到count值,则发送警报
webhook可以简单理解为是一种Web层面的回调机制,通常由一些事件触发,与代码中的事件回调类似,只不过是web层面的,由于是web层面的,所以当事件发生时,回调的不再是代码中的方法或者函数,而是服务接口。例如在告警这个场景中,告警就是一个事件。当该事件发生时,Skywalking就会自动去调用一个配置好的接口,该接口就是所谓的webhook。
Skywalking的告警消息会通过http请求进行发送,请求方法为post,Content-Type为application/json,其JSON数据是基于List
官网参考:https://github.com/apache/skywalking/blob/v8.5.0/docs/en/setup/backend/backend-alarm.md
json格式例子:
[{
"scopeId": 1,
"scope": "SERVICE",
"name": "serviceA",
"id0": "12",
"id1": "",
"ruleName": "service_resp_time_rule",
"alarmMessage": "alarmMessage xxxx",
"startTime": 1560524171000
}, {
"scopeId": 1,
"scope": "SERVICE",
"name": "serviceB",
"id0": "23",
"id1": "",
"ruleName": "service_resp_time_rule",
"alarmMessage": "alarmMessage yyy",
"startTime": 1560524171000
}]
(1)写接收钩子函数的实体类
/**
* 警告类实体
*/
public class AlarmMessageEntity {
private int scopeId;
private String scope;
private String name;
private String id0;
private String id1;
private String ruleName;
private String alarmMessage;
private List tags;
private long startTime;
private transient int period;
private transient boolean onlyAsCondition;
public int getScopeId() {
return scopeId;
}
public void setScopeId(int scopeId) {
this.scopeId = scopeId;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId0() {
return id0;
}
public void setId0(String id0) {
this.id0 = id0;
}
public String getId1() {
return id1;
}
public void setId1(String id1) {
this.id1 = id1;
}
public String getRuleName() {
return ruleName;
}
public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}
public String getAlarmMessage() {
return alarmMessage;
}
public void setAlarmMessage(String alarmMessage) {
this.alarmMessage = alarmMessage;
}
public List getTags() {
return tags;
}
public void setTags(List tags) {
this.tags = tags;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public int getPeriod() {
return period;
}
public void setPeriod(int period) {
this.period = period;
}
public boolean isOnlyAsCondition() {
return onlyAsCondition;
}
public void setOnlyAsCondition(boolean onlyAsCondition) {
this.onlyAsCondition = onlyAsCondition;
}
@Override
public String toString() {
return "AlarmMessageEntity{" +
"scopeId=" + scopeId +
", scope='" + scope + '\'' +
", name='" + name + '\'' +
", id0='" + id0 + '\'' +
", id1='" + id1 + '\'' +
", ruleName='" + ruleName + '\'' +
", alarmMessage='" + alarmMessage + '\'' +
", tags=" + tags +
", startTime=" + startTime +
", period=" + period +
", onlyAsCondition=" + onlyAsCondition +
'}';
}
}
(2)钩子函数调用的后台接口服务,controller控制层,必须使用post方法
@RestController
@RequestMapping("/alarm")
public class AlarmController {
/**
* 接收钩子函数的接口,必须使用POST方式,使用实体来接收参数
* @param list
*/
@PostMapping(value="/receive")
public void receive(@RequestBody List list){
System.out.println("已经接收到告警消息:"+list.get(0).toString());
}
}
(3)在config/alarm-settings.yml中的webhooks中配置钩子函数
访问接口告警信息