基于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

<assemblyxmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"><id>bin</id><formats><format>zip</format><format>dir</format></formats><includeBaseDirectory>false</includeBaseDirectory><dependencySets><dependencySet><outputDirectory>/</outputDirectory><unpack>false</unpack><outputFileNameMapping>${artifactId}.jar</outputFileNameMapping><includes><include>${artifact}</include></includes></dependencySet><dependencySet><outputDirectory>/lib</outputDirectory><unpack>false</unpack><excludes><exclude>${artifact}</exclude></excludes></dependencySet></dependencySets><files><file><source>src/main/plugin.properties</source><destName>plugin.properties</destName></file></files>
</assembly>

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

Expand source

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.alibaba.oneagent</groupId><artifactId>one-java-agent-parent</artifactId><version>0.0.2</version><relativePath>../pom.xml</relativePath></parent><artifactId>demo-plugin</artifactId><dependencies><dependency><groupId>com.alibaba.oneagent</groupId><artifactId>one-java-agent-plugin</artifactId><version>${project.version}</version><optional>true</optional><scope>provided</scope></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version><scope>provided</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>bytekit-core</artifactId><scope>provided</scope></dependency></dependencies><build><finalName>demo-plugin</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.6</source><target>1.6</target><encoding>UTF-8</encoding><showDeprecation>true</showDeprecation></configuration></plugin><plugin><artifactId>maven-assembly-plugin</artifactId><executions><execution><id>bin</id><phase>package</phase><goals><goal>single</goal></goals><configuration><finalName>${project.artifactId}@${project.version}</finalName><appendAssemblyId>false</appendAssemblyId><descriptors><descriptor>src/main/assembly/assembly.xml</descriptor></descriptors></configuration></execution></executions></plugin></plugins></build><profiles><profile><id>local</id><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-antrun-plugin</artifactId><executions><execution><phase>package</phase><goals><goal>run</goal></goals><configuration><tasks><delete dir="${user.home}/oneagent/plugins/${project.artifactId}@${project.version}"></delete><copy todir="${user.home}/oneagent/plugins/${project.artifactId}@${project.version}"><fileset dir="${project.build.directory}/${project.artifactId}@${project.version}" /></copy></tasks></configuration></execution></executions></plugin></plugins></build></profile></profiles>
</project>

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

根据需要在premain或agentmain写实现

Expand source

import java.lang.instrument.Instrumentation;/*** * @author hengyunabc 2020-07-28**/
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();@Overridepublic 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;}@Overridepublic 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);     }@Overridepublic void start(PluginContext context) throws Exception {System.out.println("start " + this.getClass().getName());}@Overridepublic 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;/*** * @author hengyunabc 2021-08-26**/
public class DemoPluginClassLoaderHandler implements ClassLoaderHandler {@Overridepublic 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打包**

 <assemblyxmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"><id>bin</id><formats><format>zip</format><format>dir</format></formats><includeBaseDirectory>false</includeBaseDirectory><dependencySets><dependencySet><outputDirectory>/</outputDirectory><unpack>false</unpack><outputFileNameMapping>${artifactId}.jar</outputFileNameMapping><includes><include>${artifact}</include></includes></dependencySet><dependencySet><outputDirectory>/lib</outputDirectory><unpack>false</unpack><excludes><exclude>${artifact}</exclude></excludes></dependencySet></dependencySets><!--使用文件组进行打包,把plugin.properties文件和xxx.jar文件(对于已经做好的javaagent JAR文件需要放入插件的根目录下)
--><fileSets><fileSet><directory>src/main/</directory><includes><include>*.properties</include><include>*.jar</include></includes><outputDirectory></outputDirectory></fileSet></fileSets>
</assembly>

这样即可集成进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 {
...@Overridepublic 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:\Users\Administrator\oneagent:

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

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

运行

在打完包后,使用如下:

java -javaagent:C:\Users\Administrator\oneagent\core\oneagent@0.0.2\one-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,待解决。

5、两个agent如果代理了同一个类,会不会起冲突?不会,各自的增强代码会不受影响低执行

6、不同agent的类能否实现共享?回答:可以,通过exportPackages,importPackages实现类共享,然后通过反射来执行共享类的方法。

阿里one java agent的可插拔java agent运行相关推荐

  1. java 监控usb端口插拔_监控USB设备插拔

    最近做了UKey加密中设计到USB设备. 因UKEy是用来加密和执行PC与项目间通信加密的介质.从作用范围来讲不是传统意义上U盘作为存储介质来使用.其实熟悉网银驱动DR应该了解.在网银系统安全上一个最 ...

  2. java 监控usb端口插拔_如何监控某种类型的USB设备的插拔?

    用System.Managment class 试试 下面是我在网上找到的代码 出处 http://www.eggheadcafe.com/software/aspnet/31850441/c-usb ...

  3. Android HDMI audio设备插拔事件

    Android Q HDMI Audio热插拔检测 一.Android audio设备插拔事件检测 1.1两种机制的切换 1.2 Android 耳机插拔事件处理流程 1.3 InputEvent机制 ...

  4. react router官方文档_阿里开源可插拔 React 跨端框架 UmiJS

    点击上方"开发者技术前线",选择"星标" 18:30 在看 真爱 作者:Tamic  |  编辑: 可可 阿里之前开源:阿里闲鱼开源 Flutter 应用框架 ...

  5. java 可插拔注解_20200311 8. 注解和可插拔性

    8. 注解和可插拔性 8.1 注解和可插拔性 在 web 应用中,使用注解的类仅当它们位于 WEB-INF/classes 目录中,或它们被打包到位于应用的WEB-INF/lib 中的 jar 文件中 ...

  6. java徽章_java – 设计可插拔的点和徽章系统

    如何设计一个易于打开和关闭的可插拔点和徽章系统,以及易于变成自己的模块? 经过多次试验和错误后,我得出的结论是,点和徽章只是过于交织在应用程序的唯一业务逻辑中,它们无法以简单的方式外化(许多规则涉及理 ...

  7. java 可插拔注解_servlet3.1规范翻译:第8章 注解和可插拔性

    servlet3.1规范翻译:第8章 注解和可插拔性 . 这是Servlet3.0的新特性之一:这是Servlet3.0的新特性之一: 通过Annotation(注解)配置Servlet之前版本的Se ...

  8. SemVLP 单流和双流Transformer哪个好?阿里:我全都要!提出带可插拔模块的Transformer结构...

    关注公众号,发现CV技术之美 1 写在前面 大规模图像-文本对的视觉语言预训练(VLP)在跨模态表征的学习方面取得了快速的进展.现有的预训练方法要么直接将特征级的图像表示和文本表示连接起来作为sing ...

  9. oracle java连接串写法,Oracle可插拔数据库的jdbc连接串写法

    我在服务器上部署某个第三方系统的数据库的时候,服务器数据库版本为oracle 12c.我采用的方式是新建了一个实例.访问正常. 后来项目的负责人告诉我,oracle12C支持所谓的可插拔数据库.可插拔 ...

最新文章

  1. 计算机系统验证课件,回顾性验证及再验证简介(ppt 42页)
  2. Thttpd manual
  3. DC使用教程系列2-时钟的概念与环境接口面积约束脚本
  4. 语音预处理:服务器平台“部署”
  5. oracle10g 6.0更改5.2,Oracle 10G 10.2.0.1升级到10.2.0.5
  6. media适配css
  7. logback基础配置文件
  8. php用正则去掉一些固定字符,用PHP正则表达式清除字符串的空白
  9. 随想录(我们应该编写什么样的软件)
  10. python pop3_Python POP3 收取邮件
  11. 一位全加器Verilog的三种不同的描述
  12. 努力无用论?我不信。。。
  13. 信息安全技术 实验四 木马及远程控制技术
  14. 鸡为什么感染呼吸疾病 预防鸡流鼻涕打喷嚏的药
  15. Vue开发项目入门——Vue脚手架
  16. 微软股价突破70美元 达到创历史最高
  17. 注入工具 -- DSSS
  18. 【shell】shell脚本实战-sed流编辑器
  19. 给创维电视装鸿蒙,创维电视怎么安装第三方应用?这个方法轻松教你搞定
  20. 三角形已知两边一斜角时求第三边的公式

热门文章

  1. 将体育融入元宇宙,TopGoal 为 Web3 带来新体验
  2. oracle 序列缓存的作用,Oracle序列sequence 深入理解
  3. oracle streams缺点,streams 日差治理及监控
  4. 优秀程序员必读的6本专业书籍推荐
  5. label中不同颜色字体的显示
  6. qq浏览器开源播放器_使用极简的开源Web浏览器Min浏览网络
  7. 怎么使xp计算机不休眠,怎么让电脑不休眠,教您如何操作
  8. numpy创建kdtree
  9. 什么是精益创业?精益创业的核心方法有哪些?
  10. 华为P30暗光+夜景拍摄体验:实力担当夜拍小王子