admin 管理员组

文章数量: 887031

skywalking

一、skywalking是什么

skywalking是一个可观测性分析平台和应用性能管理系统,它也是基于OpenTracing规范、开源的AMP系统。Skywalking提供分布式跟踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。支持Java

二、SkyWalking 分为三个核心部分:

1、Agent(探针):Agent 运行在各个服务实例中,负责采集服务实例的 Trace 、Metrics 等数据,然后通过 gRPC方式上报给SkyWalking后端。

2、OAP:SkyWalking 的后端服务,其主要责任有两个。
一个是负责接收 Agent 上报上来的 Trace、Metrics 等数据,交给 Analysis Core (涉及SkyWalkingOAP 中的多个模块)进行流式分析,最终将分析得到的结果写入持久化存储中。SkyWalking 可以使用ElasticSearch、H2、MySQL等作为其持久化存储,一般线上使用ElasticSearch 集群作为其后端存储。
另一个是负责响应 SkyWalking UI 界面发送来的查询请求,将前面持久化的数据查询出来,组成正确的响应结果返回给 UI界面进行展示。

3、UI 界面:SkyWalking 前后端进行分离,该 UI 界面负责将用户的查询操作封装为 GraphQL 请求提交给 OAP后端触发后续的查询操作,待拿到查询结果之后会在前端负责展示。

三、教程

安装agent

四、Skywalking的几大特点:

  1. 多语言自动探针,Java,.NET Core和Node.JS。
  2. 多种监控手段,语言探针和service mesh。
  3. 轻量高效。不需要额外搭建大数据平台。
  4. 模块化架构。UI、存储、集群管理多种机制可选。
  5. 支持告警。
  6. 优秀的可视化效果。

五、Open Tracing

OpenTracing中最核心的概念就是Trace

1、Trace
在广义上,一个trace代表了一个事务或者流程在(分布式)系统中的执行过程。在OpenTracing标准中,trace是多个span组成的一个有向无环图(DAG),每一个span代表trace中被命名并计时的连续性的执行片段。Trace表示一个调用链路,由全局唯一的TraceID标识。Trace由Span组成。

2、 Span
(一般)表示一个函数调用,由全局唯一的SpanID标识。Span组成的Trace实际上是一颗树结构,除了根Span外,每个Span都会有一个父Span。

Span里面的信息包括:操作的名字,开始时间和结束时间,可以附带多个 key:value 构成的 Tags(key
必须是String,value可以是 String, bool 或者数字),还可以附带 Logs 信息(不一定所有的实现都支持)
也是 key:value形式。

3、Metric和Tracing的区别
Metrics和Tracing属于开箱即用的一套API,其目的是为了监控、跟踪程序调用。下面看看二者的区别

Metric主要用来进行数据的统计,比如HTTP请求数的计算。 Metrics即度量指标。其原理是,将要监测的值记录在特定的全局变量中,然后通过HTTP的形式向外提供查询这些指标的接口,即Exporter。Prometheus会通过Exporter拉取这些数据,并存放在时序数据库中,可通过PromQL进行查询。

2、Tracing即链路跟踪。其目的是在分布式系统中,完成一个用户请求可能需要多个子系统之间的相互调用,而子系统为了实现高可用都会有多个节点,导致排查问题非常困难。Tracing相对于Metrics来说,实现会更复杂

六、启动带有agent相关的服务

通过注入agent的方式启动skywalking_mysql服务,注意skywalking_mysql要打成jar包

java -javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent_mysql/skywalking-agent.jar -jar skywalking_mysql.jar

让Skywalking以指定appname的方式启动skywalking_mysql,最终的结果就是我们在skywalking后台可以看到多了一个skywalking_mysql服务

java -javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent_mysql/skywalking-agent.jar -Dskywalking.agent.service_name=skywalking_mysql -jar skywalking_mysql.jar &

七、项目中获取traceId

<!--skywalking trace工具包-->
<dependency><groupId>org.apache.skywalking</groupId><artifactId>apm-toolkit-trace</artifactId><version>${skywalking.version}</version>
</dependency>

引入这个就可以在系统中通过TraceContext.traceId() 来获取traceId,如果不引,只是在系统不能通过代码去获取,但是在skywalking也是可以采集到的。

七、排除不需要监控的端点

有的端点不需要监控,比如Swagger相关的端点,要把他排除掉:

java -javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar 
-Dskywalking.agent.service_name=skywalking_plugins 
-Dskywalking.trace.ignore_path=/exclude jar skywalking_plugins.jar &

通过命令启动服务,Dskywalking.trace.ignore_path标识不监控的url

八、告警功能

Skywalking每隔一段时间根据收集到的链路追踪的数据和配置的告警规则(如服务响应时间、服务响应时间百分比)等,判断如果达到阈值则发送相应的告警信息。发送告警信息是通过调用webhook接口完成,具体的webhook接口可以使用者自行定义,从而开发者可以在指定的webhook接口中编写各种告警方式,比如邮件、短信等。告警的信息也可以在RocketBot中查看到。

默认的告警规则配置,位于skywalking安装目录下的config文件夹下 alarm-settings.yml 文件中:

.png

比如定义:

  1. 最近3分钟内服务的平均响应时间超过1秒
  2. 最近2分钟服务成功率低于80%
  3. 最近3分钟90%服务响应时间超过1秒
  4. 最近2分钟内服务实例的平均响应时间超过1秒
@RestController
public class WebHooks {private List<AlarmMessage> lastList = new ArrayList<>();//产生告警时调用的接口@PostMapping("/webhook")public void webhook(@RequestBody List<AlarmMessage> alarmMessageList){lastList = alarmMessageList;}//为了方便,新增一个接口展示告警的信息接口@GetMapping("/show")public List<AlarmMessage> show(){return lastList;}
}

注意一定要书写一个url=/webhook的接口

九、搭建自己的java agent工程

public class PreMainAgent {/*** 在这个 premain 函数中,开发者可以进行对类的各种操作。* 1、agentArgs 是 premain 函数得到的程序参数,随同 “– javaagent”一起传入。与 main函数不同的是,* 这个参数是一个字符串而不是一个字符串数组,如果程序参数有多个,程序将自行解析这个字符串。* 2、Inst 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。** java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口,也是这个包的核心部分,* 集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。类中提供两个静态方法,方法名均为premain,不能拼错。在pom文件中添加打包插件:* @param agentArgs* @param inst*/public static void premain(String agentArgs, Instrumentation inst) {System.out.println("=========premain方法执行1========");System.out.println(agentArgs);}/*** 如果不存在 premain(String agentArgs, Instrumentation inst)* 则会执行 premain(String agentArgs)* @param agentArgs*/public static void premain(String agentArgs) {System.out.println("=========premain方法执行2========");System.out.println(agentArgs);}
}

类中提供两个静态方法,方法名均为premain,不能拼错。

<build><plugins><plugin><artifactId>maven-assembly-plugin</artifactId><configuration><appendAssemblyId>false</appendAssemblyId><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs><archive><!--自动添加META-INF/MANIFEST.MF --><manifest><addClasspath>true</addClasspath></manifest><manifestEntries><Premain-Class>PreMainAgent</Premain-Class><Agent-Class>PreMainAgent</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-RetransformClasses></manifestEntries></archive></configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin></plugins>
</build>

再把项目打包package,这样就得到一个agent的jar

在IDEA的VM options中添加代码

-javaagent:路径\java-agent-demo-1.0-SNAPSHOT.jar=HELLOAGENT

十、ByteBuddy

Byte Buddy致力于解决字节码操作和instrumentation API的复杂性。Byte Buddy提供了额外的API来生成Java agent,可以轻松的增强我们已有的代码。注意是增强代码,比如我们要统计方法调用时间,他可以通过类似于切面一样的东西增强我们的目标代码

在我们自己的agent项目中添加依赖

<dependencies><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy</artifactId><version>1.9.2</version></dependency><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy-agent</artifactId><version>1.9.2</version></dependency>
</dependencies>
public class PreMainAgent {public static void premain(String agentArgs, Instrumentation inst) {//创建一个转换器,转换器可以修改类的实现//ByteBuddy对java agent提供了转换器的实现,直接使用即可AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {return builder.method(ElementMatchers.<MethodDescription>any()) // 拦截任意方法.intercept(MethodDelegation.to(MyInterceptor.class));//拦截到的方法委托给TimeInterceptor}};//Byte Buddy专门有个AgentBuilder来处理Java Agent的场景new AgentBuilder.Default().type(ElementMatchers.nameStartsWith("com.agent"))// 根据包名前缀拦截类.transform(transformer)// 拦截到的类由transformer处理.installOn(inst);}
}

先生成一个转换器,ByteBuddy提供了java agent专用的转换器。转换器的条件如下
1、实现Transformer接口利用builder对象来创建一个转换器。转换器可以配置拦截方法的格式,比如用名称,本例中拦截所有方法
2、定义一个拦截器类MyInterceptor。创建完拦截器之后可以通过Byte Buddy的AgentBuilder建造者来构建一个agent对象。AgentBuilder可以对指定的包名前缀来生效,同时需要指定转换器对象。

public class MyInterceptor {@RuntimeTypepublic static Object intercept(@Origin Method method, @SuperCall Callable<?> callable)throws Exception {long start = System.currentTimeMillis();try {//执行原方法return callable.call();} finally {//打印调用时长System.out.println(method.getName() + ":" + (System.currentTimeMillis() - start) + "ms");}}
}

MyInterceptor就是一个拦截器的实现,统计的调用的时长。参数中的method是反射出的方法对象,而callable就是调用对象,可以通过callable.call()方法来执行原方法。

链路追踪对比

日志采集:

本文标签: skywalking