skywalking&自定义java agent

前言

公司工程是以Docker的方式部署的,并使用了K8S做的容器编排,其中容器镜像是通过Dockerfile制作的
下面列出Dockerfile的部分内容

FROM  XXXXX
MAINTAINER XXXXX
WORKDIR /root/
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone
COPY ./target/*.jar /root/
ENTRYPOINT ["sh", "-ec", "exec java -server ${JAVA_OPTS} -javaagent:/root/skywalking/agent/skywalking-agent.jar -Dnacos.config.bootstrap.enable=true 
	-Dskywalking.agent.service_name=qygk-quota 
	-Dskywalking.collector.backend_service=XXXX:XXXX 
	-jar XXX-XXX-0.0.1-SNAPSHOT.jar"]
  • 在 ENTRYPOINT 中有两段变量我一直不知道是什么意思
-Dskywalking.agent.service_name=qygk-quota
-Dskywalking.collector.backend_service=XXXX:11800

虽然我不知道什么意思 ,但是里面的skywalking我认识啊,感觉肯定有关系,所以我就特意去学了一下skywalking的内容,最后不但弄明白了这两段配置的含义,还学习到了java agent的内容。

  • 下面分享学习笔记 (资料来源于XXX官网,嘿嘿不能白占人家便宜)

相关资料我上传到了百度云,有300M左右的大小

链接:https://pan.baidu.com/s/1kdXyJEj5O7__7P2fmMGl4A
提取码:qhpc
里面是案例代码,skywalking二进制安装包,es二进制安装包,测试jar包

skywalking

  • 在说skywalking之前需要先了解一下APM系统

APM

  • APM系统,是一个应用性能管理系统,可以通过他来监控系统的行为,分析系统的性能,以便在故障时能够快速定位。

主流的开源APM产品:

  • PinPoint 是由一个韩国团队实现并开源,针对Java编写的大规模分布式系统设计.
    官方网站:
    https://github.com/naver/pinpoint

  • SkyWalking 是apache基金会下面的一个开源APM项目,为微服务架构和云原生架构系统设计。
    官方网站:
    http://skywalking.apache.org/

  • Zipkin是由Twitter开源,是分布式链路调用监控系统。
    官方网站:
    https://zipkin.io/

  • 以上的产品他们都遵从一个规范: OpenTracing

OpenTracing

分布式追踪系统的理念最先由Google在Dapper论文中提出,接着各个厂商根据Dapper论文实现了一套自己的分布式链路追踪系统,而OpenTracing提供了一套与厂商平台无关的API,使得开发人员能够快速添加和更换分布式追踪系统的实现。

  • 为了更好的说明OpenTracing在分布式追踪系统中的作用,我直接从网上抄了两个图。

  • 一般我们的分布式系统调用是这样的

在这里插入图片描述

  • 这种不能直观的看到每次调用的情况,如果调用链在长一点,整体则会显示的非常复杂,无法直观的看到每次调用的状态和时间。
  • 如果是下面这样 ,则能非常直观的展示调用链之间的状态和时间关系
    在这里插入图片描述
  • 而使用OpenTracing则可以非常轻松的构建出上面这种直观的调用链。

skywalking概念

  • 官方描述:Skywalking是一个可观测性分析平台(Observability Analysis Platform简称OAP) 和应用性能管理系统(Application Performance Management简称APM)。
  • 特点:
  1 多语言自动探针(这个会在下面详细说到) JAVA、NODE.JS等
  2 可视化效果优秀
  3 整体架构模块化,存储集群多种方案可选
  4 多种监控,探针,server mesh
  5 支持告警功能
  • 架构图(ps:网上抄来的)

在这里插入图片描述
这个图可以分成四个部分来看

  • skywalking系统的UI界面
    在这里插入图片描述
  • 数据分析平台 OAP(Observability Analysis Platform)
    在这里插入图片描述
Tracing和Metrice 表示他是用来接收作用在我们的服务上的Tracing和Metrics数据。
通过gRPC远程调用的方式将数据传入OAP
Analysis Core 分析引擎,用来作数据的分析,整合等运算处理
Query Core 用来查询处理过的数据,返回给UI界面展示
  • 探针从服务端收集的数据
    在这里插入图片描述
探针 java agent 会被安装在我们的服务上,后面会告诉大家怎么安装,很简单的一行参数。
探针会从我们服务上获取Tracing和Metrics数据
Tracing数据:我们每一次请求的开始到结束,他会将我们这次请求过程中的范围,涉及到的所有数据,元数据信息等绑定到单个事务中进行处理。
Metrics数据:他是一个聚合数据,这些聚合数据都具有原子性,且可累加,聚合数据都是累加出来的。每个数据都是一个逻辑计量单元。
	例如我们一个接口在一秒中内被调用的次数,在这一秒钟内每次请求过来,他都会更新自己的统计。
  • 存储部分
    在这里插入图片描述
这些是skywalking支持的数据持久化的方案
他默认使用的H2 是通过内存缓存来操作的。
而我们后面会将其换成ES。

关于Tracing和Metrics的概念我们可以参考
http://blog.oneapm.com/apm-tech/811.html.
里面讲了Tracing和Metrics和Logging三者的关系

  • 小结:skwalking是通过探针,在我们的服务上搜集Tracing和Metrics数据,在通过OAP数据分析平台的Analysis Core 分析引擎,对数据进行处理,然后存入持久层,接着我们可以通过skywalking的UI界面向OAP的Query Core 发生HTTP请求,他会将数据从持久层给我们返回UI界面,供我们查看。

  • 以上来看skywalking的总体可以分成三部分

  1. 探针,被安置在服务上,用来获取Tracing和Metrics数据。
  2. OAP数据分析平台,用来作数据的分析,然后存入持久层,在通过UI界面查询展示
  3. skywalking的UI界面,用来作数据的展示。

skywalking的三个概念性术语

  • 服务(Service)
    我们的独立应用就是一个服务,即我们单个应用搭建的服务集群,这个集群就是一个服务
  • 端点(Endpoint)
    我们的接口,每个请求的接口就是一个端点
  • 实例(Instance)
    每个集群内的单个服务,也就是单个应用不同的节点,就是一个实例

skywalking搭建(二进制方式)

GitHub安装文档:https://github.com/apache/skywalking/blob/master/docs/en/setup/README.md
相关资料可以从上面百度云地址下载。

  • skywalking 版本: 6.5.0(要求ES 6.3.2版本以上)
  • ES版本:6.4.0 (直接安装需要java环境)
  • JAVA环境:JDK1.8
  • Centos 7.6 设置了2G内核 3.5G内存 50G硬盘
  • 测试服务均为SpringBoot工程,也会告诉大家tomcat如何配置
  1. 创建目录
    命令 : mkdir /mnt/skwalking
  2. 将资源全部上传至 /mnt/skwalking/ 目录下
    在这里插入图片描述
  3. 安装并启动ES(需要java8 环境 自行安装)
    命令: tar -zxvf ./elasticsearch-6.4.0.tar.gz
    修改Linux系统权限设置
Linux系统限制应用创建的文件是65535 而ES需要65536
默认限制root用户开启任意线程数,而其他用户开启1024 但是ES需要4096+

因为某种原因,生产环境我们是不允许使用root用户启动这些中间件的。
这里案例也保持一致

  • 创建用户,名称为es
    创建命令: useradd es
    设置密码: passwd es
    然后输入你的密码,过于简单的密码会有错误提示,但仍然可以设置成功。(不要使用123456)

  • 修改文件和创建线程的权限
    命令 vim /etc/security/limits.conf (没有vim 命令的 使用vi 就行)
    在文件末尾加上下面的内容

es soft nofile 65536
es hard nofile 65536
es soft nproc 4096
es hard nproc 4096

效果图
在这里插入图片描述
保存退出。(颜色是使用vim自带的,vi是没有的,如果没vim可以通过 yum -y install vim 安装)

  • 修改ES虚拟空间的使用权限
修改系统控制权限
es需要需要开辟一个65536字节以上的空间虚拟内容 linux不允许任何用户和进程直接开辟这么大的虚拟内存

命令 vi /etc/sysctl.conf
文件加入以下内容

vm.max_map_count=262144

效果图

在这里插入图片描述
执行 sysctl -p 让刚才的配置生效

  • 将刚才的解压出来的ES目录 权限赋予es用户
    命令 chown -R es elasticsearch-6.4.0
  • 切换es用户启动es
    命令 su es
  • 进入 ES的bin目录执行 启动脚本
    命令 cd elasticsearch-6.4.0/bin/
    启动 ./elasticsearch -d (-d 表示后台运行)
    可以查看启动日志 tail -f …/logs/elasticsearch.log(当前是在bin路面下 …/logs 是切换到上级目录的 logs )
  • 测试es是否启动成功
    命令行输入 curl http://localhost:9200
    返回以下内容是启动成功
{
  "name" : "fqXNBBj",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "WhmkRPrfTb-pMsSgxGHfuQ",
  "version" : {
    "number" : "6.4.0",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "595516e",
    "build_date" : "2018-08-17T23:18:47.308994Z",
    "build_snapshot" : false,
    "lucene_version" : "7.4.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}
  1. 安装skywalking
    skwalking 包含两部分,一部分是后台服务,一部分是UI前端服务。
  • 切换root用户 回到/mnt/skywalking/ 下 解压skywalking安装包
su root 会提示你输入密码
cd /mnt/skywalking  进入skywalking
tar -zxvf apache-skywalking-apm-6.5.0.tar.gz  解压
cd apache-skywalking-apm-bin  进入刚解压的目录
  • 我们前面说过skywalking是默认使用H2数据库 他是内存数据库,我们将他切换为ES的
  • 修改配置
vim ./config/application.yml 编辑 application.yml
找到H2数据的内容 将其注释掉 输入 /H2 可以在文件中搜索快速定位

在这里插入图片描述

  • 找到ES的内容 将其注释打开,他的配置就在H2的上面
    在这里插入图片描述
  • 因为我们的ES就是本地地址和默认端口,不用设置任何内容,打开注释后保存退出即可
  • 先启动OAP后台服务
当前目录执行命令   bin/oapService.sh
  • 查看UI界面的配置文件
vim webapp/webapp.yml

在这里插入图片描述
使用8080端口,保持默认,你也可以根据需要改变端口,保存退出

  • 启动UI界面
当前目录执行  
/bin/webappService.sh

然后通过浏览器 IP+端口的形式访问
在这里插入图片描述

启动成功,你们应该是没有数据的 ,我展示的内容是之前测试的数据结果。搭建完成

shywakling基础使用

  • 探针agent
探针可以让我们在不改变代码的情况下,对java应用使用到的组件进行动态监控,获取运行时期数据。
探针在java中使用的是java agent 是用java实现的,使用是不需要修改代码。
java agent 是通过虚拟机vm接口,在运行时修改代码。
  • skywalking的 agent 支持java1.6 -12 的版本,与agent相关的配置文件都放置在agent目录下
    在这里插入图片描述
activations 
bootstrap-plugins
config  配置文件
logs  日志
optional-plugins  可选插件,里面的插件是不生效的。因为一些插件不是必要的而且性能不高,所有skywalking将这些插件单独放置。
	如果需要使用可以将里面的插件移动到plugins里面。
plugins  组件的所有插件,里面的插件都会生效的
skywalking-agent.jar agent配置在我们的服务中的jar包

下面我们来使用skywalking

skywalking的使用

在这里插入图片描述
我们使用这三个服务来演示

  • skywalking_springboot.jar

启动:

java -javaagent:/mnt/skwalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar 
	-Dskywalking.agent.service_name=springboot 
		-Dserver.port=8082 -jar skywalking_springboot.jar &

命令解读:

-javaagent:/mnt/skwalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar  
	指定上文我提到过的作用在java服务上的 agent的位置
-Dskywalking.agent.service_name=springboot 
	指定探针名字,后续与该服务相关的数据都会以改名字展现。 这也就是我们的Dockerfile那两段配置的含义
-jar skywalking_springboot.jar  就是启动springboot应用
& 表示在后台启动 日志还是会打印在前台,但是我们退出 应用依然会在后台运行

在这里插入图片描述

  • 内部提供了两个接口 :
/sayBoot  返回Hello Boot!
/exception 返回的是一个除0异常

我们分别请求两个端口  我们是以8082端口启动的
	http://ip:8082/sayBoot
	http://ip:8082/exception

再次查看skywalking
在这里插入图片描述

  • 当前服务展示的就是我们 在启动时加入 -Dskywalking.agent.service_name=springboot 指定的探针名字
  • 端点为我们请求的接口
  • 当前实例展示当前的服务所在IP和Pid。
    在这里插入图片描述

追踪可以看到请求的调用链,和时长。这个调用链不长,我们换成另外两个jar包测试。
在这里插入图片描述
这两个是springboot集成dubbo。一个生产者一个消费者

  • 按照刚才我启动的方式 大家可以自己尝试一下。
  • (命令有空格 复制后需要调整一下)
  • skywalking_dubbo_provider.jar
java -javaagent:/mnt/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar 
	-Dskywalking.agent.service_name=dubbo_provider 
		-Dserver.port=8083 -jar skywalking_dubbo_provider.jar & 
  • skywalking_dubbo_consumer.jar
java -javaagent:/mnt/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar 
 	-Dskywalking.agent.service_name=dubbo_consumer
 		-Dserver.port=8084 -jar skywalking_dubbo_consumer.jar & 
  • 浏览器请求地址
http://ip:8085/hello

在这里插入图片描述

  • 查看skywalking

在这里插入图片描述

这下就看到了,调用链每一层的请求,与关系。

  • 关于skywalking的使用部分放在最后写,下面进入skywalking的agent原理部分。

java agent

  • 概念:
    java agent 是java命令的一个参数,他可以指定一个jar包,当java虚拟机启动时,在执行main函数之前,jvm会先运行jar包内 PremainClass 类的premain方法 。
    这个类不一定叫做 PremainClass,是通过打包插件指定某个类,但是类中一定要有premain方法
  • jar包条件
  1. 必须有MANIFEST.MF文件,必须包含 PremainClass类选项。
  2. PremainClass类选项指定的类必须包含permain方法。

自定义java agent

  • 一般我们在统计某个方法或者某个接口的调用时长的时候,会使用SpringAOP技术(笔者是做Java的,只知道这玩意)。
  • 我们可以仿照skywalking通过java agent来做这个链路时长统计
  • 为了降低使用操作字节码技术的的成本 Byte Buddy对其进行了封装开发了一套java agent 提供我们使用,我们就使用其来操作
  • 创建java agent工程 (maven 工程)
  • pom文件 加入以下内容
 <dependencies>
 	<!--ByteBuddy 坐标-->
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.10.0</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.10.0</version>
        </dependency>
    </dependencies>
        <build>
            <plugins>
                <plugin>
		    <!--使用的插件名字-->
                    <artifactId>maven-assembly-plugin</artifactId>
                    <!--插件配置信息-->
                    <configuration>
                        <appendAssemblyId>false</appendAssemblyId>
                        <descriptorRefs>
			<!--无论这个项目添加什么依赖 都会被打入jar包-->
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                        <archive>
			<!--自动添加META-INF/MANIFEST.MF-->
                            <manifest>
                                <addClasspath>true</addClasspath>
                            </manifest>
                            <manifestEntries>
                            <!--指定包含premain方法的类-->
                                <Premain-Class>PreMainAgent</Premain-Class>
                                <Agent-Class>PreMainAgent</Agent-Class>
                                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                <Can-Retransform-Classes>true</Can-Retransform-Classes>
                            </manifestEntries>
                        </archive>
                    </configuration>
                    <executions>
                        <execution>
                            <id>make-assembly</id>
                            <phase>package</phase>
                            <goals>
                                <goal>single</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
  • 创建PreMainAgent类 内包含permain方法
  • PreMainAgent
/**
 * premain 有两个参数的  premain(String agentArgs, Instrumentation instrumentation)
 * 还有一个参数的 写法为 premain(String agentArgs)
 * 当两个方法都存在的时候 默认调用两个参数的方法 。当只有一个参数的方法存在时,调用一个参数的方法。
 */
public class PreMainAgent{

    /**
     *
     * @param agentArgs
     * @param instrumentation 个是java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口 也是这个包的核心
     *                           集中了其中几乎所有的功能方法,例如类定义的转换和操作等
     */
    public static void premain(String agentArgs, Instrumentation instrumentation){
        // 我们需要创建一个转换器,修改类的实现
        // ByteBuddy对java agent 提供了转换器的实现 为AgentBuilder.Transformer 方法
        AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {  // 这是一个接口
            public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
                // builder.method 指定哪些方法被拦截  需要ElementMatchers类型的参数
                // ElementMatchers.<MethodDescription>any() 是所有方法被拦截
                // intercept 自定义拦截器 需要MethodDelegation类型的参数
                return builder.method(ElementMatchers.<MethodDescription>any())
                        .intercept(MethodDelegation.to(MyInterceptor.class));//我们自定义的拦截器类
            }
        };
            //创建完拦截器后 通过ByteBuddy的AgentBuilder创建者来构建 agent 对象 指定拦截的类型 同时还需要指定转换器对象
            // type属性 指定拦截的类型  这里我们选择 以 "com.ips" 开头的包 内的类都拦截
        new AgentBuilder.Default().type(ElementMatchers.<TypeDescription>nameStartsWith("com.ips"))
                            // 传入上面我们创建的转换器 将拦截到的类交给transformer来处理
                            // installOn 将该方法 加载到 Instrumentation 让他根据我们的配置生效
                            .transform(transformer).installOn(instrumentation);
    }
}
  • 自定义的拦截器类MyInterceptor
public class MyInterceptor {

    /**
     * 因为这个方法是在运行中进行的 所以我们要加上 @RuntimeType
     * 方法需为静态方法
     * @param method  第一个参数是反射获取的方法 需要使用@Origin注解
     * @param callable 第二个参数为Callable 需要@SuperCall 注解
     * @return 返回类型为Callable对象 但是我们不清楚对象是什么状态的 所以这里使用Object
     * @throws Exception
     */
    @RuntimeType
    public static Object intercept(@Origin Method method,
                                   @SuperCall Callable<?> callable) throws Exception {
        // 获取当前系统时间
        Long start = System.currentTimeMillis();
        //定义字符串
        String msg;
        try {
            // 获取当前执行方法的结果
            return callable.call();
        }finally {
            //计算方法执行时间  /1000.00  毫秒转换为秒 保留两位小数
            double end = (System.currentTimeMillis() - start)/ 1000.00;
            //如果超过一秒钟
            if ( end > 1.0){
                msg = "已超时。";
            }else {
                msg = "正常";
            }
            System.out.println(method.getName()+" 函数执行时长 : "+ end + "秒.   状态 : " + msg);
        }
    }
}
  • 工程结构

在这里插入图片描述

使用maven打包
在这里插入图片描述

  • 记录jar包位置
    在这里插入图片描述

  • 创建测试工程
    在这里插入图片描述

  • 注意包的名字 要与java agent premain方法里面指定的一样

  • 编写main方法


public class JavaAgentTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main执行");
        // 睡眠1.5秒
        Thread.sleep(1500L);

    }
}
  • 添加启动参数
- javaagent:C:\Users\92862\Desktop\skywalking\javaagentTime\target\javaagentTime-1.0-SNAPSHOT.jar
  • 添加位置
    在这里插入图片描述- 如果大家没有这个 可以先把你创建的工程main方法执行一次就有了。
    进入 ,这个位置添加启动参数。然后保存退出
    在这里插入图片描述

  • 再次启动main方法测试

在这里插入图片描述

  • 成功

skywalking的使用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
图里面写错了 是左键 我已经开始迷糊了

在这里插入图片描述如果你使用了Skywalking提供我们Trace工具包

引入坐标



<dependency>            

	<groupId>org.apache.skywalking</groupId> 

	<artifactId>apm-toolkit-trace</artifactId>

	<version>6.5.0</version> 

</dependency>

API



//使当前链路报错,并且提示报错信息
       

ActiveSpan.error(new RuntimeException("Test-Error-Throwable"));
       

//打印info信息  
     

ActiveSpan.info("Test-Info-Msg");      
 

//打印debug信息  
     

ActiveSpan.debug("Test-debug-Msg");

//返回traceId        

return TraceContext.traceId();

这些详细信息都会在上图的那个位置展示出来

API 返回的traceId 也可以在这个位置搜索出来
在这里插入图片描述

告警功能
默认的告警规则配置
位于skywalking安装目录下的config文件夹下 alarm-settings.yml 文件
大家可以照着这个表自己看

在这里插入图片描述

在配置文件中还有这么一个接口
在这里插入图片描述
到触发告警的时候 他会去请求你写入的接口。

只支持post请求。

生产过程中,我们可以在该接口里面调用一些短信,邮件等服务,通知处理人员,提高故障处理速度。

指标对比 。选择服务 和粒度 (粒度比如:响应时间这些)
在这里插入图片描述

在上文中给大家看了 一个概念图
在这里插入图片描述

  • 我们讲过一个请求就是一个tracing 这里也是的
  • 每个颜色块 也叫做span,span是具有开始时间和执行时长的逻辑单元
(抄的)
Span里面的信息包括:操作的名字,开始时间和结束时间.
可以附带多个 key:value 构成的 Tags(key 必须是String,value可以是 String, bool 或者数字).
还可以附带 Logs 信息(不一定所有的实现都支持) 也是 key:value形式。 
  • span内的概念 (抄的)
一个span可以和一个或者多个span间存在因果关系。OpenTracing定义了两种关系:
ChildOf 和 FollowsFrom 。
这两种引用类型代表了子节点和父节点间的直接因果关系。未来,OpenTracing将支持非因果关系的span引用关系。(
例如:多个span被批量处理,span在同一个队列中,等等)
ChildOf 很好理解,就是父亲 Span 依赖另一个孩子 Span。比如函数调用,被调者是调用者的孩子,比如说 RPC 调用,服务端那边的Span,就是 ChildOf 客户端的。
很多并发的调用,然后将结果聚合起来的操作,就构成了 ChildOf 关系。 
如果父亲 Span 并不依赖于孩子 Span 的返回结果,这时可以说它他构成 FollowsFrom 关系。
  • Log的概念(抄的)
    每个span可以进行多次Logs操作,每一次Logs操作,都需要一个带时间戳的时间名称,以及可选的任 意大小的存储结构。

  • Tags的概念 (抄的)
    每个span可以有多个键值对(key:value)形式的Tags,Tags是没有时间戳的,支持简单的对span进行 注解和补充。
    如下图就是一个Tags的详细信息,其中记录了数据库访问的SQL语句等内容。
    在这里插入图片描述

到这结束 写不下去了

发布了11 篇原创文章 · 获赞 5 · 访问量 3428

猜你喜欢

转载自blog.csdn.net/qq_44647212/article/details/104424394