JMeter 源码解读 [8] - BeanShell Server

上一次在分析 JMeter 核心类的时候 (http://markshao.github.io/2019/03/07/jmeter/) 讲到了无论是单机还是分布式模式下都会调用 startOptionServer, 我们来看一下到底是启动了那个server

startOptionServer 的逻辑很简单,先简单看一眼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void startOptionalServers() {
int bshport = JMeterUtils.getPropDefault("beanshell.server.port", 0);// $NON-NLS-1$
String bshfile = JMeterUtils.getPropDefault("beanshell.server.file", "");// $NON-NLS-1$ $NON-NLS-2$
if (bshport > 0) {
log.info("Starting Beanshell server ({},{})", bshport, bshfile);
Runnable t = new BeanShellServer(bshport, bshfile);
t.run(); // NOSONAR we just evaluate some code here
}

runInitScripts();

int mirrorPort=JMeterUtils.getPropDefault("mirror.server.port", 0);// $NON-NLS-1$
if (mirrorPort > 0){
log.info("Starting Mirror server ({})", mirrorPort);
try {
Object instance = ClassTools.construct(
"org.apache.jmeter.protocol.http.control.HttpMirrorControl",// $NON-NLS-1$
mirrorPort);
ClassTools.invoke(instance,"startHttpMirror");
} catch (JMeterException e) {
log.warn("Could not start Mirror server",e);
}
}
}

核心的逻辑就是现在配置文件中读取相关 beanshell 的配置文件,然后调用Runnable t = new BeanShellServer(bshport, bshfile); 来启动一个BeanShell服务

什么是BeanShell呢,它的官方介绍在这里 http://www.beanshell.org/bshservlet.html,总结下来就是一个 Java Runtime 的一个hook服务,提供类似 oo 的 script 脚本作为interface和外部交互,实际的作用就是可以用来对于 runtime 的 jvm 做一些动态的挑战,例如获取一些jmeter中的变量,或者修改一些变量来改变最后的JMeter 的运行行为等

BeanShellServer 是一个对于 beanshell 服务的封装,它实现了Runnable接口,但是从startOptionServer的代码里来看,没看到有 Thread 的调用

1
2
Runnable t = new BeanShellServer(bshport, bshfile);
t.run(); // NOSONAR we just evaluate some code here

我们在BeanShellServer中的run函数看到,它是通过反射来调用bsh的服务,可能在这里会有一个 thread 的处理吧

1
2
Class<?> interpreter = loader.loadClass("bsh.Interpreter");//$NON-NLS-1$
Object instance = interpreter.getDeclaredConstructor().newInstance();