研究一下skywalking+elasticsearch实现分布式链路追踪微服务
备注:
skywalking版本使用的是6.6.0
elasticsearch的版本6.8.3
skywalking对es的版本号有一定的要求,最好使用6.3.2以上版本,如果是7.x的版本,需要额外进行配置。
1.基础概念
skywalking是一个分布式链路追踪技术,为服务(service),服务实例(service instance),端点(endpoint)提供了观测能力。
服务:可以理解为saas的user,store,coupon等微服务
服务实例:例如正式环境ops部署了6台2核4G的机器,每台机器就是一个实例
端点:理解为ops的一个接口url
2.搭建方法
windows系统搭建不断报错,所以直接在linux系统搭建
2.1.搭建elasticsearch
这里需要注意下es的集群端口号的问题,es的集群端口号默认是9300,根据一些博客所说,不可以修改集群的第一个节点的端口号,否则会导致后面的节点无法加入到集群中。
2.1.1.搭建linux系统的es
1.修改系统中允许应用最多创建多少文件等的限制权限,linux默认,一般限制应用最多创建的文件是65536个,而es至少需要65536的文件创建数权限。
2.修改系统中允许用户启动的进程开启多少个线程。默认的linux限制root用户开启的进程可以开启任意数量的线程,其他用户开的的进程可以开启1024个线程,必须修改限制数为4096+。因为es至少需要4096的线程池预备。
vi /etc/security/limits.conf
#新增以下配置
es soft nofile 65536
es hard nofile 65536
es soft nproc 4096
es hard nproc 4096
3.修改系统控制权限,es需要开辟一个65536个字节以上空间的虚拟内存,linux默认不允许任何用户和应用程序直接开辟这么大的虚拟内存。
vi /etc/sysctl.conf
#新增
vm.max_map_count = 262144
保存后退出 vi编辑器
让系统控制权限生效
sysctl -p
4.新建一个用户,用于es的启动。
因为es在5.x以上的版本之后,强制要求在linux中不能使用root用户启动es进程,所以必须使用其他用户启动es才可以。
#创建用户
useradd es
#修改密码,自定义一个密码
passwd es
#修改elasticserach目录的拥有者 ,以下命令最后一个是es的安装目录名称
chmod -R es elasticsearch-6.8.3
#切换用户 es
#进入elasticsearch的安装目录下bin文件夹,通过elasticsearch.sh来启动es
启动完成之后,可以通过curl http://localhost:9200观看是否启动成功
2.2.linux系统搭建skywalking
2.2.1.搭建skywalking
#切换到root用户
su root
解压skywalking,切换到skywalking的安装目录中,如下图所示,skywalking需要改动的配置主要是config目录下的application.yml和webapp目录下的webapp.yml
2.2.2.修改skywalking的配置文件
#修改skywalking的配置文件,修改skywalking存储的数据源配置,全部改成es并且注释H2的配置,如下图2所示。
vi config/application.yml
修改webapp目录下的webapp.yml中的端口号,主要的避免端口号冲突:我改成了9010
2.2.3.启动skywalking
切换到skywalking的bin目录下,启动startup.sh
这时候skywalking目录下会出现logs目录,进入其中,可以看到启动日志webapp.log
注意下确认,你的云服务器有没有开通9010端口。
如果没有开通的话,去云服务器的安全组去开放端口号,如果已经开通了,那么可以通过你的服务器的ip+端口号就能访问到skywalking了。
2.3.测试skywalking监控demo微服务
写一个demo微服务测试一下skywalking的监控。
2.3.1.介绍skywalking的agent
首先,注意下skywalking目录下的agent目录,其中/agent/config/目录下的agent.config是配置文件,plugins是默认的所有的组件插件,optional-plugins是可选的插件,最后
skywalking-agent.jar这个jar包是最重要的。启动微服务的时候,需要指明这个jar包的位置。
注意:微服务可以共用一个agent。也可以每个微服务单独使用各自的agent,可以通过命令 cp -r agent agent_ops复制。
共用一个agent的话,使用
java -javaagent:/path/skywalking-agent.jar -Dskywalking.agent.service_name=某某 -jar 微服务.jar &
或
java -javaagent:/path/skywalking-agent.jar=agent.service_name=某某 -jar 微服务.jar &
2.3.2.修改agent/config/agent.config配置文件
修改的是agent.service_name这个值,目前改成testdemo,以后上线的话,可以修改成ops,order等等微服务的名称
2.3.3.编写testdemo
我写的这个springboot微服务的端口号是8899
package com.skywalking.testdemo.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SkyWalkingDemoController {
public static final Logger log = LoggerFactory.getLogger(SkyWalkingDemoController.class);
@GetMapping("getOrderNo")
public String getOrderNoTest4SkyWalking() {
return "dushanTest122212321312";
}
@GetMapping("/exception")
public String testException(){
int i =1/0;
return "hello exception";
}
}
打包,上传到服务器,jar包名称testdemo.jar,启动jar包,注意命令如下:
java -javaagent:/data/apache-skywalking-apm-bin/agent/skywalking-agent.jar -jar testdemo.jar &
启动成功,注意你的服务器有没有开通8899端口号,然后在浏览器上访问写的这两个接口。
这个时候再去访问skywalking,就能看到调用结果。
注意:一定要注意右下角的时间,坑的要死,之前试验了很多次,都没有结果,最后发现这个右下角的时间选项有问题,选择你测试的那个时间段,就能看到结果了。
2.4.skywalking页面参数意义
2.4.1.首页
2.4.1.1.查看:
2.4.1.2.按照服务维度查看:
2.4.1.3.按照端点维度查看:
2.4.1.4.按照实例维度查看
还能看到JVM,GC和CPUI占用率:
2.4.2.拓扑图
点击拓扑图,可以看到当前服务的连接和调用情况,之后还可以看服务间的调用情况:
2.4.3.追踪
点开页面中的追踪页面,蓝色代表,接口调用成功,红色代表接口调用失败,并且页面最上,可以选择服务,实例,状态,进行筛选,
点击页面右侧接口,就能看到接口调用详情。例如点击上图中的exception
2.4.4.告警
上线的时候可以选择是否配置
2.4.4.1.简介:
skywalking每隔一段时间根据收集到的链路追踪的数据和配置的告警规则(服务器响应时间,服务响应时间的百分比)等,判断如果达到阈值则发送响应的告警信息,发送告警信息是通过调用webhook接口完成,具体的webhook接口可以自定义,从而开发者可以在指定的webhook接口中编写各种告警方式,邮件,短信等,告警信息,也可以在RocketBot中查看到。
默认的告警规则,在skywalking目录下的config目录下的alarm-setting.yml文件,如下,我删除了无用的注释。
# Sample alarm rules.
rules:
# Rule unique name, must be ended with `_rule`.
#下面这个代表对服务的相应时间做的一个规则
service_resp_time_rule:
#oal脚本中的度量名称,这里代表的是服务的相应时间
metrics-name: service_resp_time
op: ">"
#1秒
threshold: 1000
#多久检查一次,默认是10分钟
period: 10
#达到多少次后发送报警消息
count: 3
#在多久之内,忽略报警信息,下面这个代表,当这个告警发生之后,在5分钟内不会重复发送
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_p90_sla_rule:
# Metrics value need to be long, double or int
metrics-name: service_p90
op: ">"
threshold: 1000
period: 10
count: 3
silence-period: 5
message: 90% response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes
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
# 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/
2.4.4.2.新增超时接口和回调接口
package com.skywalking.testdemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SkywalkingAlarmController {
//超时告警
@GetMapping("timeout")
public String timeout(){
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "timeout";
}
}
基础类:
注意这个基础类,skywalking6.x和5.x的版本,基础类不同,所以如果你的基础类缺少字段,可能导致回调接口没有起作用。我的版本是6.x。
不同的版本去github中的skywalking项目中的
skywalking/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/alarm/AlarmMessage.java类中去看字段
package com.skywalking.testdemo.pojo;
public class AlarmMessage {
private int scopeId;
private String scope;
private String name;
private int id0;
private int id1;
private String ruleName;
private String alarmMessage;
private long startTime;
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 int getId0() {
return id0;
}
public void setId0(int id0) {
this.id0 = id0;
}
public int getId1() {
return id1;
}
public void setId1(int 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 long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
}
回调接口:
package com.skywalking.testdemo.controller;
import com.alibaba.fastjson.JSON;
import com.skywalking.testdemo.pojo.AlarmMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
public class WebHooks {
public static final Logger log = LoggerFactory.getLogger(WebHooks.class);
private List<AlarmMessage> lastList = new ArrayList<>();
//接受到报警消息后,传给参数list
@RequestMapping(value = "/webhook", method = RequestMethod.POST)
public void webhook(@RequestBody(required = false) List<AlarmMessage> alarmMessageList) {
log.info("list ={}", JSON.toJSONString(alarmMessageList));
lastList = alarmMessageList;
}
//浏览器显示报警消息
@GetMapping("/show")
public List<AlarmMessage> show() {
return lastList;
}
}
2.4.4.3修改服务器中的skywalking的告警规则
修改回调接口,并重启:
默认的告警规则是,超过3次,都超时,就触发告规则,那就多次调用接口
ip:8899/timeout
再接着就能在skywalking中的告警页面看到告警信息:
再调用ip:8899/show就能看到告警信息的list
2.4.5.指标对比
用于当出现异常的时候,对两个实例之间进行对比,看看这两个实例,在相应时长等信息是否在一个维度,如果不在一个等级上,则某个实例可能出现了问题。
2.5.skywalking监控mysql
新增接口,访问数据库中的user表,user表就2个字段,id和username.
链接数据库的配置就不卸载文档中了。重新打包,上传,重启。
package com.skywalking.testdemo.controller;
import com.skywalking.testdemo.dao.UserRepository;
import com.skywalking.testdemo.pojo.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@RestController
public class Skywalking4MysqlController {
@Resource
private UserRepository userRepository;
@GetMapping("/userList")
public List<User> finadAll() {
List<User> list = new ArrayList<>();
userRepository.findAll().forEach((user) -> {
list.add(user);
});
return list;
}
}
pojo:
package com.skywalking.testdemo.pojo;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
@Table("user")
public class User {
@Id
private Long id;
private String username;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
repository:
package com.skywalking.testdemo.dao;
import com.skywalking.testdemo.pojo.User;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Integer> {
}
在浏览器中访问接口
再次刷新skyywalking仪表盘首页,可以看到这个接口调用情况如下图:
切换到数据库页面
进入到追踪页面,点击右侧的接口,可以看到sql语句和sql语句的执行时间
点击1的结果:
点击2的结果:
2.6.获取skywalking的追踪ID
引入jar包:
<properties>
<java.version>1.8</java.version>
<skywalking.version>6.6.0</skywalking.version>
</properties>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>${skywalking.version}</version>
</dependency>
修改原先2.3.3的接口:增加:TraceContext.traceId()
package com.skywalking.testdemo.controller;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SkyWalkingDemoController {
public static final Logger log = LoggerFactory.getLogger(SkyWalkingDemoController.class);
@GetMapping("getOrderNo")
public String getOrderNoTest4SkyWalking() {
log.info("getOrderNoTest4SkyWalking function start and traceId = {}", TraceContext.traceId());
return "dushanTest122212321312";
}
@GetMapping("/exception")
public String testException() {
try {
int i = 1 / 0;
return "hello exception";
} catch (Exception e) {
log.error("traceId = {},exception of testException ={}", TraceContext.traceId(), e.getMessage());
}
return "fuck error";
}
}
打包,上传,运行,访问。
再次调用接口,就能在日志中获取skywalking的traceId
复制这个traceId,可以去skywalking的追踪页面进行搜索,查询到接口的调用情况