栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

阿里one java agent的可插拔java agent运行

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

阿里one java agent的可插拔java agent运行

基于one java agent的可插拔java agent方案 背景

第⼀阶段:⾃研微服务
阿⾥巴巴的微服务拆分实践进⾏的很早,从 2008 年就开始了,当时的单体应⽤已经⽆法承载业务迭代的速度,由五彩⽯项⽬开始了微服务化的改造,在这个改造过程中,也逐步诞⽣了服务框架,消息队列,数据库分库分表等三⼤中间件。在这个阶段的服务治理能⼒是通过 SDK⽅式直接依赖在框架⾥⾯的。每个中间件都有⾃⼰独⽴的 SDK 依赖,服务治理能⼒的升级需要借助框架 SDK 的升级来解决,升级成本是很⾼的。

第⼆阶段:Fat-SDK
随着中间件接⼊数量的增加,业务升级成本不断攀升,从 2013 年起诞⽣了代号 “Pandora”的项⽬,主要有 2 个⽬标,⼀是解决中间件和业务依赖的冲突问题,⼆是解决服务治理升级效率的问题。同⼀个组件,业务和中间件的可能依赖不同的版本,最常⻅的例如⽇志,序列化组件等等,如果⼤家共享⼀个版本则会出现中间件的升级影响到业务,或者出现不兼容的情况。Pandora 提供了⼀个轻量的隔离容器,通过类加载器隔离的⽅式,将中间件和业务的依赖互相隔离,⽽中间件和中间件之间的依赖也能互相隔离。另外,通过 Fat-SDK 的⽅式,将所有中间件⼀次性打包交付给业务⽅升级。这⼀点和 Maven 引⼊的 bom 的思路类似,但是相⽐bom 来说每个 Pandora 的插件都可以享有独⽴的依赖。通过这种⽅式,业务不再需要单独升级某个中间件,⽽是⼀次性把所有的中间件完成升级,从⽽⼤幅提升了中间件升级的效率。

第三阶段:One Java Agent
随着业务的进⼀步发展,中间件的数量逐步增加,Pandora 的⽅式也遇到了相当多的问题,也就是如果要把⼀个 Pandora 的版本在全集团内全部推平,需要⻓达 1 年的时间才能完成。这是因为即使是 Pandora 的⽅式,也需要业务修改代码,升级,验证,发布,这些并⾮业务真正关⼼,业务更希望专注于⾃身业务的发展。通常借助双⼗⼀⼤促这样的机会,才有可能完成中间件的升级。这也给服务治理的形态带来新的挑战。2019 年,阿⾥推出了 One Java Agent 的形态,把服务治理的能⼒下沉到 Java Agent 的形式,通过⽆侵⼊的⽅式,实现了中间件的迭代升级,进⼀步提升了升级效率。

在One Java Agent中, 各个中间件的代码能够独⽴开发、部署,且尽可能做到互不影响,其有以下几种特性:

  • 每个 plugin 可以由启动参数来单独控制是否开启。
  • 各个 plugin 的启动是并⾏的,将 java agent 的启动速度由 O(n+m+…)提升⾄O(n)。
  • 各个 plugin 的类,都由不同的类加载器加载,最⼤限度隔离了各个 plugin,解决了各个agent可能出现的依赖冲突问题。
  • 每个 plugin 的状态都可以上报到服务端,可以通过监控来检测各个 plugin 是否有问题。

One Java Agent 的 开源地址:https://github.com/alibaba/one-java-agent

使用 下载源码

下载源码:https://github.com/alibaba/one-java-agent,下载后的源码如下图所示

其中核心包为one-java-agent、one-java-agent-plugin、one-java-agent-spy,其他为示例demo工程。

开发agent插件

由于one-java-agent需要统一维护和管理插件,因此需要将需管理的agent插件加入one-java-agent工程体系中,按照接入场景分,一般为几种场景:

  • 未开发的agent接入
  • 已开发的agent jar接入
  • 已开发的agent源码接入

下面分别针对以上三中情况详细的开发说明:

未开发的agent接入

即将要开发agent可按照one-java-agent的开发规范接入one-java-agent体系,主要有以下步骤:

1**、新建子模块工程**

工程文件需包括以下几部分:

**打包配置类:**assembly文件夹以及assembly.xml

**插件配置类:**plugin.properties

**agent****启动类:**DemoAgent

**插件激活器:**PluginActivator

插件所需的类加载处理器:DemoPluginClassLoaderHandler

**pom****依赖文件:**pom.xml

2**、配置打包配置类**

将plugin配置文件以及当前代码打包,一般默认即可

Expand source

    bin
    
        zip
        dir
    
    false
    
        
            /
            false
            ${artifactId}.jar
            
                ${artifact}
            
        
        
            /lib
            false
            
                ${artifact}
            
        
    
    
        
               src/main/plugin.properties
            plugin.properties
        
    

3**、引入pom.xml文件依赖**

Expand source



    4.0.0
    
        com.alibaba.oneagent
        one-java-agent-parent
        0.0.2
        ../pom.xml
    
    demo-plugin
 
    
        
            com.alibaba.oneagent
            one-java-agent-plugin
            ${project.version}
            true
            provided
        
 
        
            org.slf4j
            slf4j-api
        
 
        
            ch.qos.logback
            logback-classic
        
        
            ch.qos.logback
            logback-core
        
 
        
            com.alibaba
            fastjson
            1.2.76
            provided
        
 
        
            com.alibaba
            bytekit-core
            provided
        
    
    
        demo-plugin
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.6
                    1.6
                    UTF-8
                    true
                
            
 
            
                maven-assembly-plugin
                
                    
                        bin
                        package
                        
                            single
                        
                        
                            ${project.artifactId}@${project.version}
                            false
                            
                                src/main/assembly/assembly.xml
                            
                        
                    
                
            
 
        
    
 
    
        
            local
            
                
                    
                        org.apache.maven.plugins
                        maven-antrun-plugin
                        
                            
                                package
                                
                                    run
                                
                                
                                    
                                        
                                        
                                        
                                            
                                        
                                    
                                
                            
                        
                    
                
            
        
    

 

4**、编写agent启动类**

根据需要在premain或agentmain写实现

Expand source

import java.lang.instrument.Instrumentation;
 
 
public class DemoAgent {
 
        public static void premain(String args, Instrumentation inst) {
               init(true, args, inst);
        }
 
        public static void agentmain(String args, Instrumentation inst) {
               init(false, args, inst);
        }
 
        public static synchronized void init(boolean premain, String args, Instrumentation inst) {
               System.out.println("demo-plugin demo-agent started.");
        }
 
}

5**、编写插件激活器PluginActivator**

PluginActivator激活器需实现enabled(控制是否启动),init(初始化),start(开始),stop(结束)接口,对插件实现控制以及生命周期的监测。

Expand source

import com.alibaba.bytekit.ByteKit;
import com.alibaba.fastjson.JSON;
import com.alibaba.oneagent.plugin.PluginActivator;
import com.alibaba.oneagent.plugin.PluginContext;
 
public class DemoActivator implements PluginActivator {
    private String name = this.getClass().getSimpleName();
 
    @Override
    public boolean enabled(PluginContext context) {
        System.out.println("enabled " + this.getClass().getName());
        System.err.println(this.getClass().getSimpleName() + ": " + JSON.toJSONString(this));
 
        System.err.println("bytekit url: " + ByteKit.class.getProtectionDomain().getCodeSource().getLocation());
        return true;
    }
 
    @Override
    public void init(PluginContext context) throws Exception {
        // 注册自定义的ClassLoaderHandler,让被增强的类可以加载到指定的类
        ClassLoaderHandlerManager loaderHandlerManager = context.getComponentManager().getComponent(ClassLoaderHandlerManager.class);
        loaderHandlerManager.addHandler(new DemoPluginClassLoaderHandler());
 
           System.out.println("init " + this.getClass().getName());
               Instrumentation instrumentation = context.getInstrumentation();
        String args = context.getProperty("args");
        DemoAgent.init(true,args,instrumentation);     }
 
    @Override
    public void start(PluginContext context) throws Exception {
        System.out.println("start " + this.getClass().getName());
    }
 
    @Override
    public void stop(PluginContext context) throws Exception {
        System.out.println("stop " + this.getClass().getName());
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
}

6**、编写DemoPluginClassLoaderHandler类加载处理类**:

Expand source

 
import com.alibaba.oneagent.service.ClassLoaderHandler;
 

public class DemoPluginClassLoaderHandler implements ClassLoaderHandler {
 
    @Override
    public Class loadClass(String name) {
        if (name.startsWith("com.activator.test")) {
            try {
                Class clazz = this.getClass().getClassLoader().loadClass(name);
                return clazz;
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

7**、配置插件配置类**

Expand source

specification=1
name=demo-plugin
version=1.0.0
classpath=demo-plugin.jar:lib/
pluginActivator=com.activator.test.DemoActivator
importPackages=com.alibaba.fastjson,com.alibaba.bytekit

其中name、version、pluginActivator根据实际情况指定。

–实际例子参照代码工程中的demo-plugin子模块

已开发的agent jar接入

对于已开发好的agent jar包,同样需要新建一个插件管理,新建的配置参考上面的章节,有以下几点区别:

​ 1**、不用写 agent启动类,因为xxx-agent.jar已经有相关代码了**

​ 2**、需将agent.jar文件直接放入与plugin.properties同级别的目录中**

​ 3**、需在plugin.properties中指定jar路径,这样one-java-agent才可以启动此jar包**

4**、需在assembly.xml中指定将jar打包**

 
    bin
    
        zip
        dir
    
    false
    
        
            /
            false
            ${artifactId}.jar
            
                ${artifact}
            
        
        
            /lib
            false
            
                ${artifact}
            
        
    
 

    
        
            src/main/
            
                *.properties
                *.jar
            
            
        
    

这样即可集成进one-java-agent体系中,实际例子参照代码工程中的opentelemetry-plugin子模块

已开发好的agent源码接入

对于已开发完毕的agnet源码工程,也需跟场景一一样新建子工程,只不过跳过新的agent启动类编写,而去改造已有的agent启动类以及修改新编写的插件激活器PluginActivator。改造如下:

一般来说,一个已开发好的Agent 源码,它会有一个包含 premain 函数的启动类,假设为MyAgent类:

public class MyAgent {
    public static void premain(String args, Instrumentation inst) {
        // do something
    }
}

可以先把原来的初始化逻辑抽取为init函数,把原来的初始化逻辑移到里面,例如:

public class MyAgent {
 
    public static void premain(String args, Instrumentation inst) {
        init(args, inst);
    }
 
    public static void init(String args, Instrumentation inst) {
        // do something
    }
}

然后按上面的文档,在插件激活器PluginActivator的init函数里调用原来的MyAgent.init(args, instrumentation);函数

public class MyActivator implements PluginActivator {
...
    @Override
    public void init(PluginContext context) throws Exception {
        Instrumentation instrumentation = context.getInstrumentation();
        String args = context.getProperty("args");
        MyAgent.init(args, instrumentation);
    }
...
}
参数传递方式

对于agent插件所需的参数,一般有如下几种传递方式:

1、配置到插件的plugin.properties中,通过PluginContext#getProperty("key1")来获取值

2、通过-D参数配置,比如插件aaa,则可以配置为-Doneagent.plugin.aaa.key1=value1,然后可以通过PluginContext#getProperty("key1")来获取值。

3、配置在环境变量中,使用System.getenv(”xxx“)获取,如opentelemetry通过在启动脚本中export设置环境变量,opentelemetry agent会从环境变量中获取。

4、通过-D参数传递,然后在代码中用System.getProperty(“xxxx”)获取,如elastic apm agent则是通过-D参数传递,

打包

编译后使用mvn clean package -P local -DskipTests会打包后安装最新到本地 ~/oneoneagent 目录下如C:UsersAdministratoroneagent:

其中core存放的是one-java-agent的jar包,在使用时直接使用此agent做java-agent

而plugins则是存放相关可插拔的插件:

运行

在打完包后,使用如下:

java -javaagent:C:UsersAdministratoroneagentcoreoneagent@0.0.2one-java-agent.jar -jar ./springboot-mybatis2-1.0-SNAPSHOT.jar

可以通过-D来指定参数,可以指定的参数如下:

  • oneagent.verbose 打印trace级别的日志,打印日志到stdout
  • oneagent.plugin.disabled 禁止指定插件启动,比如 oneagent.plugin.disabled=aaa,bbb,ccc
  • oneagent.plugin.${pluginName}.enabled 指定是否启动某个插件,比如: oneagent.plugin.aaa.enabled
Q&A

1、其他团队管理的one-java-agent打包出来的插件文件夹直接扔到统一的plugns下能用吗 。回答: 能用。

2、elastic-apm agent 使用-D的方式参数是否能传递。 回答:能

3、opentelemetry agent使用export设置环境变量是否能传递 回答:能

4、插件存在多版本的情况是否会加载最大版本 。回答: 20220428目前不会,维护者已经列出issue,待解决。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/867514.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号