让 Java Agent 在 Dragonwell 上更好用

本文是《容器中的Java》系列文章之 3/n ,欢迎关注后续连载 :) 。

背景

Java Agent 技术能够动态修改 Java 应用程序行为,而不用重新修改代码。

正是因为这些特点,很多中间件团队、云厂商团队、开源产品,开始使用 Java Agent 技术来提供一些基础能力,比如 Apache Skywalking、OpenTelemetry 都提供了 Java Agent。

在早前,中间件团队通过SDK提供能力(比如可观测、微服务治理能力等);但中间件团队每次新增特性、修复缺陷,都需要各个业务方更新SDK版本、重新发布。

随着公司架构越来越复杂,随着云厂商开始提供中间件能力,这种逐个推动SDK使用方更新的方式越来越麻烦。

而用了 Java Agent 之后,业务同学只需要写业务代码;中间件能力通过设置环境变量来动态注入Java Agent来实现。Java Agent的更新,也只需要重启应用即可。

问题

我们以一个微服务demo为例。先在一个Kubernetes集群中部署demo,然后通过JAVA_TOOL_OPTIONS使用用Java Agent:

1
2
$ echo $JAVA_TOOL_OPTIONS
-javaagent:/home/admin/.opt/ArmsAgent/arms-bootstrap-1.7.0-SNAPSHOT.jar ...

我们登陆容器时,就能看到注入的 Java Agent:

但这样的结果是容器中所有的JVM,都会挂载 Java Agent。

比如就执行一个java -version,也要加载 Java Agent:

还有jstack也会加载 Java Agent :

  1. java/jps/jstack/jcmd等JDK自带的问题排查工具,都会去从环境变量加载Java Agent。
  2. 因为 Java Agent 是在JVM初期加载的,所以Java Agent会先耗费6-7s来加载agent逻辑。
  3. 但作为JDK工具,其实没有任何业务逻辑,不需要微服务治理能力。可以不用加载Java Agent的。

我们设想下这个场景:线上应用出现问题了,运维同学要抓现场,上去就想jstack拉一下stacktrace信息。
结果先要加载java agent,不但浪费了CPU和内存,更容易错过问题排查的现场。

但,一边要通过环境变量无侵入注入Java Agent,一边又要不在某些进程内注入。看起来无解了?

修复

首先,注入 Java Agent 与否,是JVM确定的。我们只需要修改 JVM 的判断逻辑即可。
在这一点上,Dragonwell团队有着丰富的经验。

其次,我们看下JVM的行为,现有的开源行为如下:

  • JDK相关命令,都会从JAVA_TOOL_OPTIONS加载 Java Agent
  • OpenJDK9之后,引入了JDK_JAVA_OPTIONS,这个环境变量只会被java命令使用。jps/jstack等命令不会加载。
  • 有些JDK厂商,会有自己的扩展环境变量, 比如,IBM会读取IBM_JAVA_OPTIONS,开源后的OpenJ9开始使用OPENJ9_JAVA_OPTIONS,Oracle/OpenJDK使用_JAVA_OPTIONS。

本来JDK_JAVA_OPTIONS能够很好的满足需求,但作为“你发任你发,我用Java8”的业务开发同学,稳定为先,所以 Java 8 是一定要支持的。

经过和Dragonwell的讨(si)论(bi),确定了如下修改:

  • DRAGONWELL_JAVA_OPTIONS,和IBM_JAVA_OPTIONS类似,设置某些只用于Dragonwell的Java参数。
  • DRAGONWELL_JAVA_TOOL_OPTIONS_JDK_ONLY,和JDK_JAVA_OPTIONS类似。
    如果DRAGONWELL_JAVA_TOOL_OPTIONS_JDK_ONLY=true,则JAVA_TOOL_OPTIONS只会被java命令加载。jps/jstack不会加载JDK_JAVA_OPTIONS环境变量、不会加载Java Agent。

经过上面的改造,就可以做到只对业务Java进程加载 Java Agent 。同时不影响jps/jstack等JDK自带的运维命令。

当然,Dragonwell作为开源项目,讨论的整体流程都是在GitHub Issue上完成的,欢迎围观、吃瓜、吐槽:
https://github.com/alibaba/dragonwell8/issues/330#issuecomment-1138083844

最终效果

让我们使用最新的Dragonwell版本,跑一下业务应用,模拟一下运维场景:

可以看到,有JAVA_TOOL_OPTIONS能够保证业务进程加载 Java Agent,同时也保证了 java -version等JDK运维工具不会加载Java Agent了

总结

在云原生领域,Java Agent作为一种无侵入、低开销的运行时增强能力,业务同学不同修改一行代码即可接入可观测、微服务治理等中间件能力。让业务专注于业务价值。

作者

Robert Lu

发布于

2022-09-26

许可协议

评论