• Spring Cloud(十):Spring Cloud Skywalking


    链路追踪组件选型

    • Zipkin是Twitter开源的调用链分析工具,目前基于springcloud sleuth得到了广泛的使用,特点是轻量,使用部署简单。
    • Pinpoint是韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能强大,接入端无代码侵入。
    • SkyWalking是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能较强,接入端无代码侵入。目前已加入Apache孵化器。
    • CAT是大众点评开源的基于编码和配置的调用链分析,应用监控分析,日志采集,监控报警等一系列的监控平台工具。

    在这里插入图片描述
    在这里插入图片描述

    探针性能对比:

    在三种链路监控组件中,skywalking的探针对吞吐量的影响最小,zipkin的吞吐量居中。pinpoint的探针对吞吐量的影响较为明显

    skywalking是什么

    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/v9.1.0/readme/

    • 中文文档: https://skyapm.github.io/document-cn-translation-of-skywalking/

    • 版本: v9.2.0

    • 采集数据——》传输数据——》存储数据——》分析数据——》监控报警

    Skywalking主要功能特性

    1. 多种监控手段,可以通过语言探针和service mesh获得监控的数据;
    2. 支持多种语言自动探针,包括 Java,.NET Core 和 Node.JS;
    3. 轻量高效,无需大数据平台和大量的服务器资源;
    4. 模块化,UI、存储、集群管理都有多种机制可选;
    5. 支持告警;
    6. 优秀的可视化解决方案;

    Skywalking整体架构

    在这里插入图片描述

    整个架构分成四部分:

    • 上部分Agent :负责从应用中,收集链路信息,发送给 SkyWalking OAP 服务器;
    • 下部分 SkyWalking OAP :负责接收Agent发送的Tracing数据信息,然后进行分析(Analysis Core),存储到外部存储器(Storage),最终提供查询(Query)功能;
    • 右部分Storage:Tracing数据存储,目前支持ES、MySQL、Sharding Sphere、TiDB、H2多种存储器,目前采用较多的是ES,主要考虑是SkyWalking开发团队自己的生产环境采用ES为主;
    • 左部分SkyWalking UI:负责提供控制台,查看链路等等;

    SkyWalking支持三种探针:

    • Agent – 基于ByteBuddy字节码增强技术实现,通过jvm的agent参数加载,并在程序启动时拦截指定的方法来收集数据。
    • SDK – 程序中显式调用SkyWalking提供的SDK来收集数据,对应用有侵入。
    • Service Mesh – 通过Service mesh的网络代理来收集数据。

    后端(Backend)

    接受探针发送过来的数据,进行度量分析,调用链分析和存储。后端主要分为两部分:

    • OAP(Observability Analysis Platform)- 进行度量分析和调用链分析的后端平台,并支持将数据存储到各种数据库中,如:ElasticSearch,MySQL,InfluxDB等。
    • OAL(Observability Analysis Language)- 用来进行度量分析的DSL,类似于SQL,用于查询度量分析结果和警报。

    界面(UI)

    • RocketBot UI – SkyWalking 7.0.0 的默认web UI
    • CLI – 命令行界面

    在这里插入图片描述

    SkyWalking 环境搭建部署

    在这里插入图片描述

    • skywalking agent和业务系统绑定在一起,负责收集各种监控数据
    • Skywalking oapservice是负责处理监控数据的,比如接受skywalking agent的监控数据,并存储在数据库中;接受skywalking webapp的前端请求,从数据库查询数据,并返回数据给前端。- Skywalking oapservice通常以集群的形式存在。
    • skywalking webapp,前端界面,用于展示数据。
    • 用于存储监控数据的数据库,比如mysql、elasticsearch等。

    在这里插入图片描述
    SkyWalking APM
    Java Agent

    搭建SkyWalking OAP 服务

    1)先使用默认的H2数据库存储,不用修改配置
    config/application.yml

    2)启动脚本bin/startup.sh

    ➜  apache-skywalking-apm-bin ./bin/startup.sh
    SkyWalking OAP started successfully!
    SkyWalking Web Application started successfully!
    
    • 1
    • 2
    • 3

    日志信息存储在logs目录

    启动成功后会启动 skywalking-oap-server 和 skywalking-web-ui

    skywalking-oap-server服务启动后会暴露端口,修改端口可以修改config/applicaiton.yml

    • 11800 收集监控数据端口
    • 12800 接受前端请求端口

    skywalking-web-ui服务会占用 8080 端口, 修改端口可以修改webapp/webapp.yml

    • server.port:SkyWalking UI服务端口,默认是8080;
    • spring.cloud.discovery.client.simple.instances.oap-service:SkyWalking OAP服务地址数组,SkyWalking UI界面的数据是通过请求SkyWalking OAP服务来获得;

    SkyWalking中三个概念

    • 服务(Service) :表示对请求提供相同行为的一系列或一组工作负载,在使用Agent时,可以定义服务的名字;
    • 服务实例(Service Instance) :上述的一组工作负载中的每一个工作负载称为一个实例, 一个服务实例实际就是操作系统上的一个真实进程;
    • 端点(Endpoint) :对于特定服务所接收的请求路径, 如HTTP的URI路径和gRPC服务的类名 + 方法签名;
      在这里插入图片描述

    SkyWalking快速开始

    1. 通过jar包方式接入
      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:/root/skywalking-agent/skywalking-agent.jar
    java $JAVA_AGENT -jar springboot-skywalking-demo-0.0.1-SNAPSHOT.jar #jar启动
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    java -javaagent:/root/skywalking-agent/skywalking-agent.jar 
    -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 
    -DSW_AGENT_NAME=springboot-skywalking-demo -jar springboot-skywalking-demo-0.0.1-SNAPSHOT.jar
    
    • 1
    • 2
    • 3
    -javaagent:/root/skywalking-agent/skywalking-agent.jar
    -Dskywalking.agent.service_name=springboot-skywalking-demo
    -Dskywalking.collector.backend_service=127.0.0.1:11800
    
    • 1
    • 2
    • 3

    使用skywalking.+配置文件中的配置名作为系统配置项来进行覆盖。 javaagent参数配置方式优先级更高

    1. IDEA中使用Skywalking
      在运行的程序配置jvm参数
    -javaagent:/root/skywalking-agent/skywalking-agent.jar
    -DSW_AGENT_NAME=springboot-skywalking-demo
    -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
    
    • 1
    • 2
    • 3

    Skywalking跨多个微服务追踪 gateway(bug)

    解决方案:拷贝agent/optional-plugins目录下的gateway插件和webflux插件到agent/plugins目录

    在这里插入图片描述

    Skywalking集成日志框架

    https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/application-toolkit-logback-1.x/

    在这里插入图片描述

    引入依赖

    
    <dependency>
        <groupId>org.apache.skywalkinggroupId>
        <artifactId>apm-toolkit-logback-1.xartifactId>
        <version>8.11.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    微服务添加logback-spring.xml文件,并配置 %tid 占位符

    
    <configuration>
    
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            
            <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
                <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                    <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%nPattern>
                layout>
            encoder>
        appender>
    
        
        
        <appender name="grpc-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
            <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
                <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                    <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%nPattern>
                layout>
            encoder>
        appender>
    
        
        <root level="INFO">
            <appender-ref ref="console"/>
            <appender-ref ref="grpc-log"/>
        root>
    
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    Skywalking通过grpc上报日志 (需要v8.4.0以上)
    gRPC报告程序可以将收集到的日志转发到SkyWalking OAP服务器上

    在这里插入图片描述
    兼容ELK 配置 Logstash
    在这里插入图片描述

    Skywalking告警通知

    skywalking告警的核心由一组规则驱动,这些规则定义在config/alarm-settings.yml文件中,告警规则的定义分为三部分:

    • 告警规则:它们定义了应该如何触发度量警报,应该考虑什么条件;
    • 网络钩子(Webhook}:当警告触发时,哪些服务终端需要被通知;
    • gRPC钩子:远程gRPC方法的主机和端口,告警触发后调用;

    为了方便,skywalking发行版中提供了默认的alarm-setting.yml文件,包括一些规则,每个规则有英文注释,可以根据注释得知每个规则的作用:

    • 在最近10分钟的3分钟内服务平均响应时间超过1000ms
    • 最近10分钟内,服务成功率在2分钟内低于80%
    • 服务实例的响应时间在过去10分钟的2分钟内超过1000ms
    • 数据库访问{name}的响应时间在过去10分钟的2分钟内超过1000ms

    只要我们的服务请求符合alarm-setting.yml文件中的某一条规则就会触发告警。

    比如service_resp_time_rule规则:

    该规则表示服务{name}的响应时间在最近10分钟的3分钟内超过1000ms
    在这里插入图片描述

    • metrics-name:度量名称,也是OAL脚本中的度量名。默认配置中可以用于告警的度量有:服务,实例,端点,服务关系,实例关系,端点关系。它只支持long,double和int类型。
    • op:操作符。
    • threshold:阈值。
    • period:多久告警规则需要被检查一下。这是一个时间窗口,与后端部署环境时间相匹配。
    • count:在一个周期窗口中,如果按op计算超过阈值的次数达到count,则发送告警
    • silence-period:在时间N中触发报警后,在N -> N + silence-period这段时间内不告警。
    • message:该规则触发时,发送的通知消息。

    demo

    1. config/alarm-settings.yml
      在这里插入图片描述
    2. notify
     @RequestMapping("/notify")
     public String notify(@RequestBody Object obj) {
         //TODO 告警信息,给技术负责人发短信,钉钉消息,邮件,微信通知等
         System.err.println(obj.toString());
         return "notify successfully";
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. log
      在这里插入图片描述
    2. 控制台
      在这里插入图片描述
      参考:https://github.com/apache/skywalking/blob/master/docs/en/setup/backend/backend-alarm.md

    在这里插入图片描述

    Skywalking持久化追踪数据

    基于mysql持久化
    1. 修改config目录下的application.yml,使用mysql作为持久化存储的仓库
    2. 修改mysql连接配置
    storage:
      #选择使用mysql   默认使用h2,不会持久化,重启skyWalking之前的数据会丢失
      selector: ${SW_STORAGE:mysql}
      #使用mysql作为持久化存储的仓库
      mysql:
        properties:
          #数据库连接地址  创建swtest数据库
          jdbcUrl: ${SW_JDBC_URL:"jdbc:mysql://1ocalhost:3306/swtest"}
          #用户名
          dataSource.user: ${SW_DATA_SOURCE_USER:root}
          #密码
          dataSource.password: ${SW_DATA_SOURCE_PASSWORD:root}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 添加mysql驱动包
    ➜  apache-skywalking-apm-bin ls oap-libs/mysql-connector-java-8.0.19.jar
    oap-libs/mysql-connector-java-8.0.19.jar
    
    • 1
    • 2

    注意:需要添加mysql数据驱动包,因为在lib目录下是没有mysql数据驱动包的,所以修改完配置启动是会报错,启动失败的。
    4. 启动Skywalking 查看swtest库生成的表
    在这里插入图片描述

    同样 Elasticsearch (注意添加索引前缀 namespace)

    在这里插入图片描述

    自定义SkyWalking链路追踪

    
    <dependency>
        <groupId>org.apache.skywalkinggroupId>
        <artifactId>apm-toolkit-traceartifactId>
        <version>8.11.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 在业务方法中可以TraceContext获取到traceId
    @RequestMapping("/list")
    public List<User> list(){
    
        //TraceContext可以绑定key-value
        TraceContext.putCorrelation("name", "fox");
        Optional<String> op = TraceContext.getCorrelation("name");
        log.info("name = {} ", op.get());
        //获取追踪的traceId
        String traceId = TraceContext.traceId();
        log.info("traceId = {} ", traceId);
    
        return userService.list();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. @Trace将方法加入追踪链路

    如果一个业务方法想在ui界面的追踪链路上显示出来,只需要在业务方法上加上@Trace注解即可

    加入@Tags或@Tag为追踪链路增加其他额外的信息,比如记录参数和返回信息

    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserMapper userMapper;
    
        @Trace
        @Tag(key = "list", value = "returnedObj")
        @Override
        public List<User> list() {
            return userMapper.list();
        }
    
        @Trace
        @Tags({@Tag(key = "param", value = "arg[0]"),
                @Tag(key = "user", value = "returnedObj")})
        @Override
        public User getById(Integer id) {
            return userMapper.getById(id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    Skywalking集群部署(oap服务高可用)

    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配置
      • 可以选择性修改监听端口
    cluster:
    	selector: ${SW_CLUSTER:nacos}
      nacos:
        serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
        hostPort: ${SW_CLUSTER_NACOS_HOST_PORT:localhost:8848}
        # Nacos Configuration namespace
        namespace: ${SW_CLUSTER_NACOS_NAMESPACE:"public"}
        # Nacos auth username
        username: ${SW_CLUSTER_NACOS_USERNAME:""}
        password: ${SW_CLUSTER_NACOS_PASSWORD:""}
        # Nacos auth accessKey
        accessKey: ${SW_CLUSTER_NACOS_ACCESSKEY:""}
        secretKey: ${SW_CLUSTER_NACOS_SECRETKEY:""}
        internalComHost: ${SW_CLUSTER_INTERNAL_COM_HOST:""}
        internalComPort: ${SW_CLUSTER_INTERNAL_COM_PORT:-1}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 配置ui服务webapp.yml文件的oap-service,写多个oap服务地址
    2. 指定微服务的jvm参数 -Dskywalking.collector.backend_service=ip1:11800,ip2:11800
    3. 启动微服务测试

    JavaAgent

    引用

    -javaagent:instrument-demo/target/instrument-demo-1.0-SNAPSHOT.jar

    javassist

    1. pom
    <dependencies>
       <dependency>
           <groupId>javassistgroupId>
           <artifactId>javassistartifactId>
           <version>3.12.1.GAversion>
           <type>jartype>
       dependency>
    dependencies>
    
    
    <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.pluginsgroupId>
               <artifactId>maven-jar-pluginartifactId>
               <version>2.2version>
               <configuration>
                   <archive>
                       <manifestEntries>
                           <Project-name>${project.name}Project-name>
                           <Project-version>${project.version}Project-version>
                           <Premain-Class>com.demo.TestAgentPremain-Class>
                       manifestEntries>
                   archive>
                   <skip>trueskip>
               configuration>
           plugin>
           <plugin>
               <groupId>org.apache.maven.pluginsgroupId>
               <artifactId>maven-shade-pluginartifactId>
               <executions>
                   <execution>
                       <phase>packagephase>
                       <goals>
                           <goal>shadegoal>
                       goals>
                   execution>
               executions>
               <configuration>
                   <artifactSet>
                       <includes>
                           <include>javassist:javassist:jar:include>
                       includes>
                   artifactSet>
               configuration>
           plugin>
           <plugin>
               <groupId>org.apache.maven.pluginsgroupId>
               <artifactId>maven-compiler-pluginartifactId>
               <configuration>
                   <source>6source>
                   <target>6target>
               configuration>
           plugin>
       plugins>
    build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    1. java

    FoxAgent.java

    public class TestAgent {
        
        public static void premain(String args, Instrumentation instrumentation){
            System.out.println("premain:获取方法调用时间");
        
            // 添加 Transformer
            ClassFileTransformer transformer = new PerformMonitorTransformer();
            instrumentation.addTransformer(transformer);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    PerformMonitorTransformer.java

    public class PerformMonitorTransformer implements ClassFileTransformer {
    
        private static final Set<String> classNameSet = new HashSet<String>();
        static {
            // 直接定位
            classNameSet.add("com.demo.HelloService");
            // 间接定位   注解  继承关系
        }
    
        @Override
        public byte[] transform(ClassLoader loader,
                                String className,
                                Class<?> classBeingRedefined,
                                ProtectionDomain protectionDomain,
                                byte[] classfileBuffer) throws IllegalClassFormatException {
            try {
                String currentClassName = className.replaceAll("/", ".");
                // 只增强Set中含有的类   过滤
                if (!classNameSet.contains(currentClassName)) {
                    return null;
                }
                System.out.println("transform: [" + currentClassName + "]");
    
                CtClass ctClass = ClassPool.getDefault().get(currentClassName);
                CtBehavior[] methods = ctClass.getDeclaredBehaviors();
                for (CtBehavior method : methods) {
                    //增强方法
                    enhanceMethod(method);
                }
                return ctClass.toBytecode();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private void enhanceMethod(CtBehavior method) throws Exception {
            if (method.isEmpty()) {
                return;
            }
            String methodName = method.getName();
            // 不增强main方法
            if (methodName.equalsIgnoreCase("main")) {
                return;
            }
            // 增强@Test修饰的方法
            if(null == method.getAnnotation(Test.class)){
                return;
            }
         
            final StringBuilder source = new StringBuilder();
            source.append("{")
                        // 前置增强: 加入时间戳
                        .append("long start = System.currentTimeMillis();\n")
                        // 保留原有的代码处理逻辑
                        .append("$_ = $proceed($$);\n")
                        .append("System.out.print(\"method:[" + methodName + "]\");")
                        .append("\n")
                        // 后置增强
                        .append("System.out.println(\" cost:[\" +(System.currentTimeMillis() -start)+ \"ms]\");")
                    .append("}");
    
            ExprEditor editor = new ExprEditor() {
              @Override
              public void edit(MethodCall methodCall) throws CannotCompileException {
                  methodCall.replace(source.toString());
              }
            };
            method.instrument(editor);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    bytebuddy

    1. pom
    <dependencies>
        <dependency>
            <groupId>javassistgroupId>
            <artifactId>javassistartifactId>
            <version>3.12.1.GAversion>
            <type>jartype>
        dependency>
    
        <dependency>
            <groupId>net.bytebuddygroupId>
            <artifactId>byte-buddyartifactId>
            <version>1.5.13version>
        dependency>
    
        <dependency>
            <groupId>net.bytebuddygroupId>
            <artifactId>byte-buddy-agentartifactId>
            <version>1.5.13version>
        dependency>
    dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-jar-pluginartifactId>
                <version>2.2version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Project-name>${project.name}Project-name>
                            <Project-version>${project.version}Project-version>
                            <Premain-Class>com.demo.TestAgentPremain-Class>
                        manifestEntries>
                    archive>
                    <skip>trueskip>
                configuration>
            plugin>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-shade-pluginartifactId>
                <executions>
                    <execution>
                        <phase>packagephase>
                        <goals>
                            <goal>shadegoal>
                        goals>
                    execution>
                executions>
                <configuration>
                    <artifactSet>
                        <includes>
                            <include>javassist:javassist:jar:include>
                            <include>net.bytebuddy:byte-buddy:jar:include>
                            <include>net.bytebuddy:byte-buddy-agent:jar:include>
                        includes>
                    artifactSet>
                configuration>
            plugin>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>6source>
                    <target>6target>
                configuration>
            plugin>
        plugins>
    build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    1. java
      TestAgent
    public class TestAgent {
        
        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.<MethodDescription>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.demo"))
                    .transform(transformer)
                    .with(listener)
                    .installOn(inst);
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    TimeInterceptor

    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");
            }
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Metric Demo

    1. pom
    <dependencies>
        <dependency>
            <groupId>javassistgroupId>
            <artifactId>javassistartifactId>
            <version>3.12.1.GAversion>
            <type>jartype>
        dependency>
    dependencies>
    
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-jar-pluginartifactId>
                <version>2.2version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Project-name>${project.name}Project-name>
                            <Project-version>${project.version}Project-version>
                            <Premain-Class>com.demo.TestAgentPremain-Class>
                        manifestEntries>
                    archive>
                    <skip>trueskip>
                configuration>
            plugin>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-shade-pluginartifactId>
                <executions>
                    <execution>
                        <phase>packagephase>
                        <goals>
                            <goal>shadegoal>
                        goals>
                    execution>
                executions>
                <configuration>
                    <artifactSet>
                        <includes>
                            <include>javassist:javassist:jar:include>
                        includes>
                    artifactSet>
                configuration>
            plugin>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>6source>
                    <target>6target>
                configuration>
            plugin>
        plugins>
    build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    1. java
      TestAgent
    public class TestAgent {
        
        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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Metric

    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: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n",
                    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: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n",
                    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<GarbageCollectorMXBean> garbages = ManagementFactory.getGarbageCollectorMXBeans();
            for (GarbageCollectorMXBean garbage : garbages) {
                String info = String.format("name: %s\t count:%s\t took:%s\t pool name:%s",
                        garbage.getName(),
                        garbage.getCollectionCount(),
                        garbage.getCollectionTime(),
                        Arrays.deepToString(garbage.getMemoryPoolNames()));
                System.out.println(info);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这里插入图片描述

  • 相关阅读:
    RK3399 Android7.1修改安兔兔等第三方软件读到的内核版本信息
    类和函数的泛化、偏特化和全特化
    聊一聊redis奇葩数据类型与集群知识
    golang常用库包:Go依赖注入(DI)工具-wire使用
    贪吃蛇和俄罗斯方块游戏
    app逆向(9)|APP关于抓包的常见问题
    高斯过程的数学理解
    Advanced .Net Debugging 8:线程同步
    anacoda常用指令教程
    android api 23以上 使用 httpclient
  • 原文地址:https://blog.csdn.net/menxu_work/article/details/127866632