对于 大型 几十个、几百个微服务构成的微服务架构系统 出现问题无法快速定位、各个微服务之间的依赖关系理不清、各个微服务的接口性能很难分析、业务调用流程处理顺序理不清
skywalking是一个国产开源框架,2015年由吴晟开源 , 2017年加入Apache孵化器。skywalking是分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。SkyWalking 是观察性分析平台和应用性能管理系统,提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。
官网:http://skywalking.apache.org/
下载:http://skywalking.apache.org/downloads/
Github:https://github.com/apache/skywalking
文档: https://skywalking.apache.org/docs/main/v8.4.0/readme/
中文文档: https://skyapm.github.io/document-cn-translation-of-skywalking/
Skywalking架构的四个部分
上部分使用java的Agent机制从应用中收集链路信息 发送给SkyWalking OAP服务器
下部分SkyWalking OAP 当收到上部分发来的链路信息之后进行分析 分析结果存到外部存储器 以提供查询
右部分Storage:Tracing数据存储,目前支持ES、MySQL、Sharding Sphere、TiDB、H2多种存储器,目前采用较多的是ES,主要考虑是SkyWalking开发团队自己的生产环境采用ES为主
左部分SkyWalking UI:负责提供控制台,查看链路等等
Agent机制 是jdk1.5提供的可以获取jvm中的字节码 然后对它进行增强
在idea中使用Agent机制(探针机制)
给jvm传参 javaagent: 传入一个实现premain方法的jar 看下面代码
Agent类 加强类
package bat.ke.qq.com;
import java.lang.instrument.Instrumentation;
public class AmAgent {
public static void premain(String args, Instrumentation instrumentation){
System.out.println("premain:" + args);
}
}
加强类的pom
trace-agent bat.ke.qq.com 1.0-SNAPSHOT 4.0.0 agent-demoorg.apache.maven.plugins maven-jar-plugin2.2 ${project.name} ${project.version} bat.ke.qq.com.FoxAgent // 这里指定这个preamin会比main先运行 true
被加强类
package com.am;
public class AgentTest {
public static void main(String[] args) {
// TODO 启动服务 获取进程 memory jvm
// Instrument 定位到UserService 统计方法执行时间
// 微服务 引入nacos pom jar yml
System.out.println("hello world");
// TODO业务逻辑 UserService方法 统计方法执行时间
}
}
被加强类pom
trace-agent bat.ke.qq.com 1.0-SNAPSHOT 4.0.0 agent-demo2
然后在被加强类启动之前传入jvm参数-javaagent:D:2021java代码存放地Skywalkingagent-demotargetagent-demo-1.0-SNAPSHOT.jar=name=123
D:2021java代码存放地Skywalkingagent-demotargetagent-demo-1.0-SNAPSHOT.jar 所指的文件就是加强类的jar 然后运行被加强类可以看到效果
以上是简单使用 下面加入了修改字节码
加强类
package com.am;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
public class FoxAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("premain:获取方法调用时间");
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder> transform(DynamicType.Builder> builder,
TypeDescription typeDescription,
ClassLoader classLoader) {
return builder
// 拦截任意方法
.method(ElementMatchers.any())
// 指定方法拦截器,此拦截器中做具体的操作
.intercept(MethodDelegation.to(TimeInterceptor.class));
}
};
AgentBuilder.Listener listener = new AgentBuilder.Listener() {
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, DynamicType dynamicType) {}
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { }
@Override
public void onError(String typeName, ClassLoader classLoader, JavaModule module, Throwable throwable) { }
@Override
public void onComplete(String typeName, ClassLoader classLoader, JavaModule module) { }
};
new AgentBuilder
.Default()
// 指定需要拦截的类
.type(ElementMatchers.nameStartsWith("com.tuling"))
.transform(transformer)
.with(listener)
.installOn(inst);
}
}
加强类所用的
package com.am;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class TimeInterceptor {
@RuntimeType
public static Object intercept(@Origin Method method,
@SuperCall Callable> callable) throws Exception {
long start = System.currentTimeMillis();
try {
// 原方法执行
return callable.call();
} finally {
System.out.println(method + ": cost " + (System.currentTimeMillis() - start) + "ms");
}
}
}
加强类pom
trace-agent bat.ke.qq.com 1.0-SNAPSHOT 4.0.0 bytebuddy-demojavassist javassist3.12.1.GA jar net.bytebuddy byte-buddy1.5.13 net.bytebuddy byte-buddy-agent1.5.13 org.apache.maven.plugins maven-jar-plugin2.2 ${project.name} ${project.version} com.tuling.FoxAgent true org.apache.maven.plugins maven-shade-pluginpackage shade javassist:javassist:jar: net.bytebuddy:byte-buddy:jar: net.bytebuddy:byte-buddy-agent:jar:
被加强类
package com.am;
public class AgentBytebuddyTest {
public static void main(String[] args) {
HelloService helloService = new HelloService();
helloService.say();
helloService.say2();
}
}
被加强类所用的
package com.am;
public class HelloService {
public String say(){
System.out.println("===hello world====");
return "hello world";
}
public String say2(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello world";
}
}
被加强类pom
trace-agent bat.ke.qq.com 1.0-SNAPSHOT 4.0.0 bytebuddy-demo2
利用agent机制 实现业务零侵入 打印jvm使用情况
加强类pom
trace-agent bat.ke.qq.com 1.0-SNAPSHOT 4.0.0 Metric-tooljavassist javassist3.12.1.GA jar org.apache.maven.plugins maven-jar-plugin2.2 ${project.name} ${project.version} com.tuling.FoxAgent true org.apache.maven.plugins maven-shade-pluginpackage shade javassist:javassist:jar:
加强类
package com.am;
import java.lang.instrument.Instrumentation;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class AmAgent {
public static void premain(String agentArgs, Instrumentation inst) {
//每隔5秒打印JVM内存和GC信息
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Metric.printMemoryInfo();
Metric.printGCInfo();
}
}, 0, 5000, TimeUnit.MILLISECONDS);
}
}
加强类所用的 打印jvm使用情况实现
package com.am;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.Arrays;
import java.util.List;
public class Metric {
private static final long MB = 1048576L;
public static void printMemoryInfo() {
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
MemoryUsage headMemory = memory.getHeapMemoryUsage();
String info = String.format("nHeapMemory init: %st max: %st used: %st committed: %st use rate: %sn",
headMemory.getInit() / MB + "MB",
headMemory.getMax() / MB + "MB", headMemory.getUsed() / MB + "MB",
headMemory.getCommitted() / MB + "MB",
headMemory.getUsed() * 100 / headMemory.getCommitted() + "%"
);
System.out.print(info);
MemoryUsage nonheadMemory = memory.getNonHeapMemoryUsage();
info = String.format("NonHeapMemory init: %st max: %st used: %st committed: %st use rate: %sn",
nonheadMemory.getInit() / MB + "MB",
nonheadMemory.getMax() / MB + "MB", nonheadMemory.getUsed() / MB + "MB",
nonheadMemory.getCommitted() / MB + "MB",
nonheadMemory.getUsed() * 100 / nonheadMemory.getCommitted() + "%"
);
System.out.println(info);
}
public static void printGCInfo() {
List garbages = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean garbage : garbages) {
String info = String.format("name: %st count:%st took:%st pool name:%s",
garbage.getName(),
garbage.getCollectionCount(),
garbage.getCollectionTime(),
Arrays.deepToString(garbage.getMemoryPoolNames()));
System.out.println(info);
}
}
}
被加强类pom
trace-agent bat.ke.qq.com 1.0-SNAPSHOT 4.0.0 Metric-tool-demo
被加强类
package com.am;
import java.util.ArrayList;
import java.util.List;
public class MetricToolAgentTest {
public static void main(String[] args) {
boolean flag = true;
while (flag) {
List
SkyWalking环境搭建
下载:http://skywalking.apache.org/downloads/
目录结构:
搭建SkyWalking OAP 服务
先使用默认的H2数据库存储,不用修改配置
config/application.yml
使用vim指令进入config/application.yml搜索storage 可以看到它的下一行是selector: ${SW_STORAGE:h2}
启动脚本 bin/startup.sh
日志信息存储在logs目录
启动成功后会启动两个服务,一个是skywalking-oap-server,一个是skywalking-web-ui
skywalking-oap-server服务启动后会暴露11800 和 12800 两个端口,分别为收集监控数据的端口11800和接受前端请求的端口12800,修改端口可以修改config/applicaiton.yml
skywalking-web-ui服务会占用 8080 端口, 修改端口可以修改webapp/webapp.yml
Skywalking到这就搭建好了
然后使用
准备一个springboot程序,打成可执行jar包,写一个shell脚本,在启动项目的Shell脚本上,通过 -javaagent 参数进行配置SkyWalking Agent来跟踪微服务;
startup.sh脚本:
#!/bin/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启动
这样启动之后 访问请求 就可以在Skywalking UI界面中查看到 多个微服务 只需要添加javaagent参数指定skywalking-agent.jar这个jar文件即可
在idea中使用 也是给jvm传参
# skywalking-agent.jar的本地磁盘的路径 -javaagent:D:apacheapache-skywalking-apm-es7-8.4.0apache-skywalking-apm-bin-es7agentskywalking-agent.jar # 在skywalking上显示的服务名 -DSW_AGENT_NAME=springboot-skywalking-demo # skywalking的collector服务的IP及端口 -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.3.100:11800
自定义链路 就是可以让Skywalking可以检测到我们的业务代码
导入依赖
org.apache.skywalking apm-toolkit-trace8.4.0
在业务方法中可以TraceContext获取到traceId
@RequestMapping("/list")
public List list(){
//TraceContext可以得到Skywalking的上下文 下面的代码时绑定一个key-value
TraceContext.putCorrelation("name", "fox");
Optional op = TraceContext.getCorrelation("name");
log.info("name = {} ", op.get());
//获取跟踪的traceId
String traceId = TraceContext.traceId();
log.info("traceId = {} ", traceId);
return userService.list();
}
userService.list();会调用到下面的list 然后list方法加了@Trace注解 这样Skywalking就可以把list方法加入到链路中 @Tag注解是用来把方法返回值写到SKywalking中
@Trace @Tag(key = "list", value = "returnedObj") public Listlist(){ return userMapper.list(); } @Trace @Tags({@Tag(key = "param", value = "arg[0]"), @Tag(key = "user", value = "returnedObj")}) public User getById(Integer id){ return userMapper.getById(id); }
日志集成
logback官方配置 https://github.com/apache/skywalking/blob/master/docs/en/setup/service-agent/java-agent/Application-toolkit-logback-1.x.md
log4j官方配置 https://skywalking.apache.org/docs/main/v8.4.0/en/setup/service-agent/java-agent/application-toolkit-log4j-1.x/
log4j2j官方配置 https://skywalking.apache.org/docs/main/v8.4.0/en/setup/service-agent/java-agent/application-toolkit-log4j-2.x
Skywalking集群部署
Skywalking集群是将skywalking oap作为一个服务注册到nacos上,只要skywalking oap服务没有全部宕机,保证有一个skywalking oap在运行,就能进行跟踪。
搭建一个skywalking oap集群需要:
(1)至少一个Nacos(也可以是nacos集群)
(2)至少一个ElasticSearch(也可以是es集群)
(3)至少2个skywalking oap服务;
(4)至少1个UI(UI也可以集群多个,用Nginx代理统一入口)
1.修改config/application.yml文件
使用nacos作为注册中心
修改nacos配置
修改存储策略,使用elasticsearch7作为storage
修改elasticsearch7配置
配置ui服务webapp.yml文件的listOfServers,写两个地址
listOfServers是OAP的地址
然后springboot应用传入的jvm参数的更改
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.3.10:11800,192.168.3.12:11800



