写点什么

Byteman 调用外部类方法的实用技巧

作者:FunTester
  • 2025-03-05
    河北
  • 本文字数:2651 字

    阅读完需:约 9 分钟

Byteman 在故障测试中有广泛应用,我第一次接触它是在 Chaos Mesh 平台上,之前也写过一些相关文章。不过,正如我之前提到的,Chaos Mesh 对 Byteman 的开发支持不到 30%。今天我分享的内容是 Byteman 的另一个用法:调用第三方类的方法。


这听起来可能和故障测试关系不大,但其实 Byteman 的功能设计中,DO 执行模块是可以用来执行方法的,这为我们提供了一个很好的切入点。尽管这个需求的解决方案不一定是最优的,但在你身处 Chaos Mesh 平台,并且需要操作多个节点时,这种方法能省去不少事。


我们的需求是服务启动后,需要调用某个类的静态方法,来完成数据初始化,甚至是周期性任务的调度。看起来这个需求和故障测试没有直接关系,但 Byteman 提供的能力恰恰能帮我们解决这个问题。通过这种方法,我们可以在不修改核心代码的情况下,实现特定的方法调用。


Byteman 本身需要一个触发点来执行注入代码。Chaos Agent 提供了一个异步线程,循环执行 org.chaos_mesh.chaos_agent.TriggerThread#triggerFunc 方法,我们可以把它当做全局注入点来用。这里的核心思想是,能够通过定时触发某些代码的执行,而不是每次都手动干预。


以下是 Chaos Mesh 项目中的源代码,展示了如何实现异步线程:


// Copyright 2022 Chaos Mesh Authors.  //  // Licensed under the Apache License, Version 2.0 (the "License");  // you may not use this file except in compliance with the License.  // You may obtain a copy of the License at  //  //     http://www.apache.org/licenses/LICENSE-2.0  //  // Unless required by applicable law or agreed to in writing, software  // distributed under the License is distributed on an "AS IS" BASIS,  // See the License for the specific language governing permissions and  // limitations under the License.  
package org.chaos_mesh.chaos_agent;
public class TriggerThread extends Thread { public void run(){ loop(); }
public static void loop() { while (true) { try { Thread.sleep(5000); } catch (Exception e) { System.out.println(e.getMessage()); } triggerFunc(); } }
public static void triggerFunc() { //System.out.println("chaos agent triger function"); } }
复制代码


接下来,我们进入真正的重点:如何调用第三方类的方法。为了演示,我使用了静态方法作为案例。需要注意,这里的“第三方”指的是除了 Byteman 和 Chaos Agent 注入点以外的类,比如一些 Java 类库的静态方法,可以直接调用,但不在本次讨论范围内。


以下是我为此编写的一个简单示例:


package com.funtest.temp;  
public class BytemanDemo {
public static void main(String[] args) { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } print(234); }
}
static int print(int i) { int a = i; System.out.println("Hello Word from Byteman ,By FunTester !!!"); return a * a; }
public static void pp() { System.out.println("33333333"); }
}
复制代码


``如果我们要调用某个类的方法,使用反射是最直接的方式:


Class.forName("com.funtest.temp.BytemanDemo").getDeclaredMethod("pp").invoke(null);


事实上,以上代码可以直接执行,但在 Byteman 的 btm 文件中会报错。我猜测是由于 Byteman 使用了 java_cup 解析器,导致与反射的兼容性问题。Java CUP(构造有用的解析器)用于生成 LALR(1) 解析器,它类似于 GNU 的 Bison 或 Yacc。虽然反射代码本身没有问题,但与 Byteman 一起使用时出现了兼容性障碍。


经过一番尝试,我灵机一动,使用 ClassLoader 来加载类,从而解决了问题。代码如下:


ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();  contextClassLoader.loadClass("com.funtest.temp.BytemanDemo").getDeclaredMethod("pp").invoke(null);
复制代码


这一行代码确实有效,但在 Byteman 的 btm 文件中依旧报错。仔细查看报错信息后,我发现了一些线索,最终的 btm 文件如下:


RULE testentCLASS com.funtest.temp.BytemanDemoMETHOD printBIND buffer = ClassLoader.getSystemClassLoader().loadClass("com.funtest.temp.BytemanDemo");m = buffer.getDeclaredMethod("pp", new Class[0]);AT ENTRYIF TRUEDO System.out.println("Hello Word,FunTester");m.invoke(null,null);ENDRULE
复制代码


最终的控制台打印信息如下:


TransformListener() : handling connection on port 9091retransforming com.funtest.temp.BytemanDemoorg.jboss.byteman.agent.Transformer : possible trigger for rule testent in class com.funtest.temp.BytemanDemoRuleTriggerMethodAdapter.injectTriggerPoint : inserting trigger into com.funtest.temp.BytemanDemo.print(int) int for rule testentorg.jboss.byteman.agent.Transformer : inserted trigger for testent in class com.funtest.temp.BytemanDemoRule.execute called for testent_1:0testent executeHello Word33333333Hello Word from Byteman ,By FunTester !!!Rule.execute called for testent_1:0testent executeHello Word33333333Hello Word from Byteman ,By FunTester !!!Rule.execute called for testent_1:0
复制代码


虽然这只是一个粗略的示例,目的是为了演示如何实现功能。实际上,可以对其进行一些优化,避免每次都重复加载类,特别是在 Spring Boot 项目中,可以通过优化加载流程避免不必要的性能开销。


实际上,这个需求的最佳解决方法是定制一个 helper 类,来扩展 Byteman 的原生功能,提供一个专门的方法来调用第三方类的方法(包括类方法、成员方法,甚至构造方法)。虽然 Byteman 的使用文档没有详细讲解这一块,但未来我会有机会分享更多的优化方案。


通过 Byteman,我们不仅能进行故障注入,还能灵活地执行各种操作,帮助我们在复杂的系统环境中执行自动化任务。

发布于: 刚刚阅读数: 5
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
Byteman 调用外部类方法的实用技巧_FunTester_InfoQ写作社区