JMeter 中脚本语言是通过什么方式操作线程中的变量的?

Java 语言中动态代码执行

在用 JMeter 做性能测试时,不可避免的需要操作线程中的参数,不同的接口需要接收的输入,以及他们响应结果都需要在运行过程中做动态处理。 JMeter 提供了 JSR223 取样器/前置处理器/后置处理器, 开发者可以在其中写脚本,那脚本时如何生效的?似乎在普通的 Web 开发中很少涉及到相关的内容。

JSR 233

JSR233 是一个标准/规范,它定义了一种接口,使得 Java 应用程序能够与脚本语言(如 JavaScript、Python、Ruby 等)进行互操作,在 Java 6 中引入,并通过 javax.script 包提供。

各自实现

所有希望自己的脚本语言能在 Java 环境中运行的开发者需要提供相应的脚本引擎实现。

例如:

​ JavaScript 脚本引擎,在 Java6 到 Java14 中包含在 JDK 中(不同的版本中可以一定是同一个引擎),Java15 以及以后就移除了了它,开发之需要在项目中单独引入。

​ groovy 脚本引擎,开发者如果需要使用 groovy 作为动态语言就需要单独引入相关的依赖,Maven 为例:

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-jsr223</artifactId>
    <version>3.0.21</version>
</dependency>
具体使用:以 Groovy 为例

1、 项目中引入依赖

2、 将 Groovy 脚本准备好,一个简单的算法,写入到任意一个文件中

import java.util.stream.Collectors

List<String> getMaxLengthStrings(String s) {
        String[] target = s.split("");
        List<String> storage = new ArrayList<>();
        StringBuilder transformable = new StringBuilder();
        for (int i = 0; i < target.length; i++) {
            transformable.append(target[i]);
            for (int j = i + 1; j < target.length; j++) {
                if (transformable.toString().contains(target[j])) {
                    break;
                }
                transformable.append(target[j]);
            }
            storage.add(transformable.toString());
            transformable = new StringBuilder();
        }
        // 获取最长不重复连续字符串,可能有多个
        List<String> result = storage.stream()
                .sorted(Comparator.comparing(String::length))
                .collect(Collectors.groupingBy(String::length))
                .get(storage.stream().max(Comparator.comparingInt(String::length)).get().length());
        return result;
    }

3、 启动 Java 程序,在程序运行过程中使用脚本

public static void main(String[] args) throws ScriptException, NoSuchMethodException, IOException {
    ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
    String groovyScript = FileUtils.readFileToString(new File("/Users/xxx/algorithm.log"), "UTF-8");
    engine.eval(groovyScript);
    Invocable invocable = (Invocable) engine;
    Object obj = invocable.invokeFunction("getMaxLengthStrings", "abcccccsdfioqccabc");
    if (obj instanceof List<?>) {
        System.out.println(obj);
    }
}

​ 2、 使用 FileUtils(来自 commons-io 的实现 )方法读取本地文件中的代码脚本

​ 3、 调用 eval 方法执行脚本,此时会将脚本代码转换成中可运行的 Java 类,通过执行后返回当前类的运行结果

​ 4、通过 invokeFunction 调用脚本中全局函数, 指定函数名 getMaxLengthStrings, 指定入参: abcccccsdfioqccabc, 即可返回函数的运行结果

private static void test() throws ScriptException, NoSuchMethodException {
    ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
    List<String> list = new ArrayList<>();
    list.add("你好");
    Bindings bindings =  engine.createBindings();
    bindings.put("list", list);
    engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
    // list会使用Binding中设置的参数
    String groovyScript =   " List<String> getResult() {\n" +
                            "        list.add(\"Hello World\");\n" +
                            "        return list;\n" +
                            "    }";
    engine.eval(groovyScript, bindings);
    Invocable invocable = (Invocable) engine;
    // 调用全局函数
    Object obj = invocable.invokeFunction("getResult");
    if (obj instanceof List<?>) {
        System.out.println(obj);
    }
}
private static void test2() throws ScriptException, NoSuchMethodException {
       ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
       List<String> list = new ArrayList<>();
       list.add("小明");
       Bindings bindings =  engine.createBindings();
       bindings.put("list", list);
       engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
       String groovyScript =   "class ScriptTest {\n" +
                               "    List list\n" +
                               "    ScriptTest(List list){\n" +
                               "        this.list = list\n" +
                               "    }\n" +
                               "    List<String> ScriptTestResult(){\n" +
                               "        list.add(\"小王\")\n" +
                               "        return list\n" +
                               "    }\n" +
                               "}\n" +
                               "scriptTest = new ScriptTest(list)";
       engine.eval(groovyScript, bindings);
       Invocable invocable = (Invocable) engine;
       Object ScriptTest = engine.get("scriptTest");
       Object obj = invocable.invokeMethod(ScriptTest,"ScriptTestResult");
       if (obj instanceof List<?>) {
           System.out.println(obj);
       }
   }
其他:
性能
安全性


↙↙↙阅读原文可查看相关链接,并与作者交流