前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Byteman 调用外部类方法的实用技巧

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

作者头像
FunTester
发布2025-03-06 23:40:40
发布2025-03-06 23:40:40
2500
代码可运行
举报
文章被收录于专栏:FunTesterFunTester
运行总次数:0
代码可运行

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 项目中的源代码,展示了如何实现异步线程:

代码语言:javascript
代码运行次数:0
复制
// 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;  

publicclass 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 类库的静态方法,可以直接调用,但不在本次讨论范围内。

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

代码语言:javascript
代码运行次数:0
复制
package com.funtest.temp;  

publicclass BytemanDemo {  

    public static void main(String[] args) {  
        while (true) {  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                thrownew 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 来加载类,从而解决了问题。代码如下:

代码语言:javascript
代码运行次数:0
复制
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();  
contextClassLoader.loadClass("com.funtest.temp.BytemanDemo").getDeclaredMethod("pp").invoke(null);

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

代码语言:javascript
代码运行次数:0
复制
RULE testent
CLASS com.funtest.temp.BytemanDemo
METHOD print
BIND buffer = ClassLoader.getSystemClassLoader().loadClass("com.funtest.temp.BytemanDemo");
m = buffer.getDeclaredMethod("pp", new Class[0]);
AT ENTRY
IF TRUE
DO System.out.println("Hello Word,FunTester");
m.invoke(null,null);
ENDRULE

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

代码语言:javascript
代码运行次数:0
复制
TransformListener() : handling connection on port 9091
retransforming com.funtest.temp.BytemanDemo
org.jboss.byteman.agent.Transformer : possible trigger for rule testent in class com.funtest.temp.BytemanDemo
RuleTriggerMethodAdapter.injectTriggerPoint : insertingtriggerintocom.funtest.temp.BytemanDemo.print(int) intforruletestent
org.jboss.byteman.agent.Transformer : insertedtriggerfortestentinclasscom.funtest.temp.BytemanDemo
Rule.executecalledfortestent_1:0
testentexecute
HelloWord
33333333
HelloWordfromByteman ,ByFunTester !!!
Rule.executecalledfortestent_1:0
testentexecute
HelloWord
33333333
HelloWordfromByteman ,ByFunTester !!!
Rule.executecalledfortestent_1:0

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

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

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

FunTesterFunTester 原创精华

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FunTester 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档