如何对Apache Tomcat的性能计数进行监视
上一章节我们对JMeter的监听器组件进行了一个概况介绍,并通过实现开发一个Report(报告)组件为大家展示了监听器组件的内部工作逻辑,本篇将为大家简单介绍一下Vizualizer(监视器)组件的开发流程和工作原理,针对Vizualizer(监视器)组件的行为我们已经介绍过,主要是主动采集外部资源的性能计数来辅助完成后续的瓶颈定位与调优工作,我们就以Apache Tomcat作为监视对象,首先来说明一下如何实现对Apache Tomcat的性能计数采集方案。
JMX方案
JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架,在标准中有统一的接口规范定义,并通过管理构件(MBean)实现管理,性能资源计数属性便是一类MBean,我们可以通过它所暴露的Get方法获取各类技术属性及其所对应的值,从而完成采集过程。
Apache Tomcat这类应用服务器中间件统一实现了JMX,并常常通过它实现内部监视和管理功能,在Tomcat7版本之后,可以在其配置文件tomcat-users.xml中发现已经将JMX管理功能单独分配了用户角色:
<tomcat-users>
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<user username="tomcat" password="tomcat" roles="manager-gui"/>
<user username="script" password="tomcat" roles="manager-script"/>
<user username="jmx" password="tomcat" roles="manager-jmx"/>
</tomcat-users>
我们可以通过JConsole工具通过JMX方式接入Tomcat:
点选MBean标签页,进入MBean浏览页面:
我们就可以通过名为“java.lang:type=MemoryPool,name=PS Eden Space”的MBean中Usage属性获取JVM GC中Eden Space的内存使用性能计数。
HTTP方案
与JMX方案的通用性相比,HTTP方案相对比较特殊,应用服务器中间件往往在自身的Web管理页面中包含了一系列性能监视,比如Tomcat的管理Web页面:
包含了服务器基本信息、JVM计数、线程池计数等,因此,通过HTMLParser来解析得到对应的性能计数数值,也可以达到采集的目的。
针对应用服务器中间件的监视定义通用接口
根据上述分析,在进行插件开发时,我们可以利用两种方式进行采集,另外又由于应用服务器中间件产品还是比较多样的,因此,有必要定义一些通用的接口和抽象类:
Accessor
我们为这类采集器统一定义一个接口Accessor(存取器)来规范其行为方法,代码参考如下:
public interface Accessor {
public String getConnectorName();//获取连接器名称
public void open(String url, String username, String password) throws IOException;//打开连接
public Object access() throws IOException, AccessException;//通过连接器访问,并返回结果对象
public void shutdown() throws IOException, AccessException;//关闭连接
}
专门定义异常,代码参考如下:
//如果未执行open()操作,则抛出该Exception
public class AccessException extends Exception{
@Override
public String getMessage(){
return "accessor has been closed!";
}
}
AccessorSampleResult
在许多采集sample发生时,我们需要将一个Object对象作为结果数值进行保存,因此,需要编写一个SampleResultObject抽象类,代码参考如下:
public abstract class SampleResultObject {
protected Object resultObj = null;
public abstract Object get();//获取值
public abstract void init();//初始化
public void set(Object object){
//赋值
this.resultObj = object;
}
}
于是,我们的AccessorSampleResult就可以通过存取SampleResultObject对象来完成,代码参考如下:
public class AccessorSampleResult extends SampleResult{
//在SampleResult基础上,新增SampleResultObject类型的结果
protected SampleResultObject resultObj = null;
protected final long ts;
public AccessorSampleResult(){
ts = System.currentTimeMillis();
}
public SampleResultObject getSampleResultObject() {
return this.resultObj;
}
public void setSampleResultObject(SampleResultObject resultObj) {
this.resultObj = resultObj;
setStartTime(ts);
}
}
AccessorSampler
AccessorSampler用于实际的采集任务,代码参考如下:
public class AccessorSampler {
private String url = null;
private String username = null;
private String password = null;
public AccessorSampler(String url, String username, String password){
this.url = url;
this.username = username;
this.password = password;
}
/**
* 执行采样,并返回采样结果
* @param accessor 继承Accessor接口的连接器对象
* @return
*/
public SampleResultObject sample(Accessor accessor){
SampleResultObject resultObj = null;
try {
accessor.open(url, username, password);
resultObj = (SampleResultObject)accessor.access();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
t