出现时间 : 9月9号,晚上发布后出现

项目: dzs-**
背景: 下午开始接入Skywalking. 晚上发布后出现该问题.

错误堆栈

  1. java.lang.ClassCircularityError: java/lang/CharacterData00
  2. at java.lang.CharacterData.of(CharacterData.java:82)
  3. at java.lang.Character.toLowerCase(Character.java:6338)
  4. at java.lang.Character.toLowerCase(Character.java:6309)
  5. at java.lang.String.toLowerCase(String.java:2581)
  6. at java.lang.String.toLowerCase(String.java:2670)
  7. at com.raycloud.safe.SecurityManager.checkPermission(SecurityManager.java:235)
  8. at sun.misc.URLClassPath.check(URLClassPath.java:642)
  9. at sun.misc.URLClassPath$JarLoader.checkResource(URLClassPath.java:961)
  10. at sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:1044)
  11. at sun.misc.URLClassPath.getResource(URLClassPath.java:239)
  12. at sun.misc.URLClassPath.getResource(URLClassPath.java:292)
  13. at java.lang.ClassLoader.getBootstrapResource(ClassLoader.java:1264)
  14. at java.lang.ClassLoader.getResource(ClassLoader.java:1093)
  15. at java.net.URLClassLoader.getResourceAsStream(URLClassLoader.java:232)
  16. at org.apache.skywalking.apm.dependencies.net.bytebuddy.dynamic.ClassFileLocator$ForClassLoader.locate(ClassFileLocator.java:438)
  17. ....
  18. at org.apache.skywalking.apm.dependencies.net.bytebuddy.matcher.ElementMatcher$Junction$Disjunction.matches(ElementMatcher.java:160)
  19. at org.apache.skywalking.apm.dependencies.net.bytebuddy.matcher.ElementMatcher$Junction$Disjunction.matches(ElementMatcher.java:160)
  20. at org.apache.skywalking.apm.dependencies.net.bytebuddy.matcher.ElementMatcher$Junction$Disjunction.matches(ElementMatcher.java:160)
  21. at org.apache.skywalking.apm.agent.core.plugin.match.ProtectiveShieldMatcher.matches(ProtectiveShieldMatcher.java:46)
  22. at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$RawMatcher$ForElementMatchers.matches(AgentBuilder.java:1299)
  23. at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:10304)
  24. at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10263)
  25. at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1600(AgentBuilder.java:10029)
  26. at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10648)
  27. at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10595)
  28. at java.security.AccessController.doPrivileged(Native Method)
  29. at org.apache.skywalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10186)
  30. at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
  31. at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
  32. at java.lang.CharacterData.of(CharacterData.java:82)
  33. at java.lang.Character.toLowerCase(Character.java:6338)
  34. at java.lang.Character.toLowerCase(Character.java:6309)
  35. at java.lang.String.toLowerCase(String.java:2581)
  36. at java.lang.String.toLowerCase(String.java:2670)
  37. at com.raycloud.safe.SecurityManager.checkPermission(SecurityManager.java:235)
  38. at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1294)
  39. at java.lang.System.getProperty(System.java:717)
  40. at com.raycloud.agent.raycloud.task.VpcHostReplaceTask$2.run(VpcHostReplaceTask.java:61)
  41. at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  42. at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
  43. at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
  44. at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
  45. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  46. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  47. at java.lang.Thread.run(Thread.java:748)

错误定位

移除 skywalking 的的 agent 监控后,恢复正常. 猜测问题出现在 双agent 上,网上出现的该错误较少,典型链接有

大致分析原因是一个 class 被重复加载了2次,具体哪里未知 , 可以推测的是 agent 执行前加载一个class,然后这个class又进入了 agent 里边进行加载.

本地复现

根据线上稳定的复现方式,下载了 skywalking 的agent配置和 公司内部的agent ,使用一个普通的 SpringBoot 开始复现.

  1. java -XX:-UseSplitVerifier -noverify -Ddns.config=k8s -Duser.home=/data/project/chenshun -javaagent:/data/project/chenshun/project-agent-1.0.0-SNAPSHOT.jar -Dskywalking.logging.dir=/data/project/chenshun/logs/skywalking -Dskywalking.agent.service_name=chenshun -javaagent:/data/project/chenshun/skywalking-agent/skywalking-agent.jar -Dskywalking_config=/data/project/chenshun/skywalking-agent/config/agent.config -jar test_web.jar

分别引入了 project-agent-1.0.0-SNAPSHOT.jar/data/project/chenshun/skywalking-agent/skywalking-agent.jar , 在10分钟的时候准时出现该错误,10-30秒后进程直接退出. 在线上的表现就是正常发布后, 10分钟开始重启.

经过对比堆栈发现,10分钟开始重启的原因是 agent 内部存在一个 10分钟 的定时任务, 且从堆栈可以看出问题出现在这里。

  1. at com.raycloud.agent.raycloud.task.VpcHostReplaceTask$2.run(VpcHostReplaceTask.java:61)

该定时任务是被一个线程池执行的,在多次测试后发现,使用 ScheduledExecutorService 就有问题

  1. ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
  2. @Override
  3. public Thread newThread(@Nonnull Runnable r) {
  4. Thread thread = new Thread(r);
  5. thread.setName("dns-陈顺");
  6. thread.setDaemon(true);
  7. return thread;
  8. }
  9. });

使用 Timernew Thread() 就没有问题. 然后使用如下方式去执行

  1. ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor()

也没有问题,最后将问题聚焦于 ThreadFactory .
这个 ThreadFactory 是一个普通的,可以被抄袭的 ThreadFactory 实现,然后拷贝 newSingleThreadScheduledExecutor#ThreadFactory 的 默认实现也没有问题,最后排查下来发现将名字设置为中文该问题必然出现,设置为英文+数字就不会出现这个问题。 根据这个结果先把agent重新打包发布上去了,使用 taobaoapi-invoke-notify 项目测试确实没有问题。

最终原因

根据线程名字和堆栈( at com.raycloud.safe.SecurityManager.checkPermission(SecurityManager.java:235))去定位哪里会去使用线程的名字,定位到 safe 会使用这个名字去加快项目启动
image.png

问题出现,然后将这部分注释,发现线程名字设置为中文也没有问题. 根据堆栈反向推导

  • 项目启动时加载定时任务的时候由于agent没有初始化完毕,所以第一次跑不会进入safe没问题。
  • 10分钟后,agent已经加载完成,每一个加载的 class 文件都会被 transform ,所以后续跑的任务进入了 agent 的加载
    • 公司的agent是采用的白名单形式,如果一个类不需要加强那么跳过.
    • skywalking采用的是判断的形式,需要进行规则判断,所以这部分堆栈很长
    • image.png
  • 从这里可以判断应该是 skywalking 经过判断后需要去加载文件 又进入了 safe 模块进行权限判断. 由此引发的2次加载问题

因此只要将 java/lang/CharacterData00skywalking 的zgent中进行跳过即可. 在skywalking的SkyWalkingAgent中加入 .or(ElementMatchers.nameStartsWith(“java.lang.”)), 项目重跑发现没有问题。