使用 WebSphere Application Server 的 Performance Monitoring Infrastructure API 编写性能监控工具
2009-11-16 00:00:00 来源:WEB开发网介绍
在如今复杂的电子商务环境中,客户需要能够监控整个生产环境的不同组件。WebSphere® Application Server,版本 4.0 让您通过它的 Performance Monitoring Infrastructure(PMI)API 来监控运行时和应用程序组件。
在本文中,我们将讨论怎样使用 PMI API 编写 WebSphere Application Server 的性能监控工具。 下面可以下载一个使用 PMI API 开发的样本命令行程序。还单独提供了 PMI API 文档的下载。
Performance Monitoring Infrastructure API
PMI API 提供了装备 WebSphere Application Server 运行时和应用程序组件的一个框架。此框架的客户端被具体化并作为轻量级 Java™ API 发布,它让您可以从装备组件搜集性能数据。这个客户端 API 由 WebSphere 资源分析器和工具供应商使用;您也可以使用它来开发自己的定制监控工具。
PMI 客户机使用 IIOP 上的 RMI 连接到 WebSphere AdminServer。另外,还提供了一个性能 servlet,用来返回 XML 格式的性能数据。
下面的图 1 展示了 WebSphere Application Server 4.0 高级版中 PMI 的客户端视图。
图 1. PMI 客户端视图
每个应用程序服务器中 PMI API 的服务器端都将性能数据作为原始计数器值保留,而客户端则检索并处理原始计数器,以提供更有意义的值,如平均值、时间加权平均值、百分比值、增量值和比率等等。这将服务器开销降至最低,并允许多个客户机之间共享服务器数据。
WebSphere Application Server 不同版本中的 PMI API 支持:
WebSphere Application Server 版本/修订版 | PMI API 支持 |
WebSphere 4.0.2 高级单服务器版 | 有,通过会话 bean |
WebSphere 4.0.1 高级版 | 有 |
WebSphere 4.0 高级单服务器版 | 没有 |
WebSphere 3.5.5 高级版 | 有,通过 4.0.2 PMI 客户机映射 |
PMI 数据组织
来自 PMI 客户机的性能数据被组织到模块中。在 WebSphere Application Server 4.0 中,PMI 提供有关下面模块的性能数据:
运行时模块
连接池— 数据库连接池
线程池— Web 容器和 ORB 线程池
会话管理器— HTTP Servlet 会话
事务管理器? 事务
JVM 运行时— 应用程序服务器 JVM
JVMPI— JVM Profiler Interface 数据
J2C— J2C 连接器
应用程序模块
EJB 模块— EJB 和它们的方法
Web 应用程序— Servlet 和 JSPs™
模块可以有子模块,每个模块/子模块都可以有相关的性能数据(也就是标准或计数器)。例如,线程池(Thread Pool)模块有两个子模块:Web Container 池和 ORB 池。这里每个子模块都会有性能数据,如池的大小、活动线程等等,而且父模块线程池将聚集来自子模块的数据。
图 2. PMI 线程池模块
每个模块都有一个相关联的装备或监控级别(最高、高、中、低和无),它们定义一个给定模块的数据集合。对于线程池模块,
“无”将禁用所有的 {}
“低”将启用 {Threads created, Threads destroyed}
“高”将启用 {Threads created, Threads destroyed, Active threads, Pool size, Percent maxed}
每个模块的装备级别都可以设置为一个希望的级别(在下面的步骤 2 中有所描述)。模块中的每个标准都有一个预先定义的级别。如果模块级别等于或高于标准级别,那么标准就会被启用。在上面的示例中,Pool Size 的级别是“高”,Threads created 的级别是“低”。
注意:WebSphere Application Server 节点和服务器被表示为没有任何性能数据的模块。
PMI 类
客户端 PMI 类在 com.ibm.websphere.pmi 包中被定义。下面是开发定制监控工具时将使用的一些 核心客户端类。请参考 PMI API 文档了解有关下面和其它可用的类的详细信息。
类 | 描述 |
com.ibm.websphere.pmi. CpdCollection | 表示一个模块或子模块。例如,线程池将是一个带有两个子集的 CpdCollection 对象:一个是 Web Container 池,另一个是 ORB 池。 |
com.ibm.websphere.pmi. CpdData | 表示性能数据。例如,Pool Size 将是 Thread 池 CpdCollection 内部可用的 CpdData 对象。 |
com.ibm.websphere.pmi. CpdValue | 表示计数器的真实数值。CpdStat 和 CpdLoad 是专门的 CpdValue,表示平均值和时间加权平均值。 |
com.ibm.websphere.pmi. PerfDescriptor | 识别一个模块(CpdCollection)或单独的计数器(CpdData)。PerfDescriptor 有 Node、Server、Module 和一个完全限定名。
例如,Web 容器线程池模块的完全限定名就可以是: flow — 节点名 DefaultServer — 应用程序服务器名 threadPoolModule — 模块名 Servlet.Engine.Transports — 子模块名(Web 容器池) |
com.ibm.websphere.pmi. PmiClient | 这是到服务器端 PMI 的主要客户端接口,它有从 WebSphere AdminServer 连接、查询和获取性能数据的方法。 |
下面的图 3 展示了上面的类之间的关系。
图 3. PMI 客户机核心类
图片看不清楚?请点击这里查看原图(大图)。
逐步了解如何使用 PMI
有了对核心 PMI 客户机类的基本了解,现在就让我们看看搜集性能数据所涉及到的步骤吧。
第一步就是连接到 WebSphere AdminServer 然后找到哪个节点、应用程序服务器和模块可以监控,下面的代码片段将帮助您确定上面所有的内容。
// Connect to WebSphere AdminServer running at hostName:900
PmiClient pmiClient = new PmiClient ("hostName", 900);
// Get Node PerfDescriptor
PerfDescriptor[] nodePd = pmiClient.listNodes ();
// Get first node name
String nodeName = nodePd[0].getName();
// Get AppServer PerfDescriptor
PerfDescriptor[] serverPd = pmiClient.listServers (nodeName);
// Get all modules in first application server
PerfDescriptor[] modulePd = pmiClient.listMembers(serverPd[0]);
现在,我们在第一个应用程序服务器中有了一个 PerfDescriptor 对应于所有模块。listMembers 调用可以递归进行,以获得子模块等。
这只是遍历到模块的一种方法。PMI 提供了 API 的一个丰富集合,允许您按照应用程序的需求以不同方式获得数据。例如,如果您知道服务器的 PMI 数据层次结构,您就可以构建一个 PerfDescriptor 然后查询服务器。
下一步是为模块启用监控(缺省设置是禁用)。每个模块都有一个装备级别(最高、高、中、低或者无),它们决定可用于监控的计数器的个数。这个设置由应用程序服务器存储和使用,而不是由 PMI 客户机存储和使用。因此,此设置是所有客户机共有的,如果有一个客户机更改了设置,就会影响到所有其它的客户机。
装备级别可以通过使用 WebSphere Application Server 管理控制台或者 WebSphere 资源分析器来设置。下面的代码举例说明了如何使用 PMI API 获取和设置装备级别。
getInstrumentationLevel() 在给定节点和服务器中递归返回所有模块和子模块的装备级别。
// Get instrumentation level
PerfLevelSpec[] level =
pmiClient.getInstrumentationLevel (nodeName, serverName);
// Get module path
String[] modulePath = level[i].getPath();
//ex: path for Thread pool module is {pmi, threadPoolModule}
// Get level
int instruLevel = level[i].getLevel();
// ex: levels defined in PmiConstants.java
现在,您可以操作这个级别数组,将级别改为想要的级别,然后调用 setInstrumentationLevel() 设置级别。
// To set ThreadPool module and sub-modules to high
boolean recursiveFlag = true;
level[i].setLevel (PmiConstants.LEVEL_HIGH);
pmiClient.setInstrumentationLevel
(nodeName, serverName, level, recursiveFlag);
另一种选择是设置一个已知模块或单独模块的级别(不用调用 getInstrumentationLevel() 方法),您可以按照下面去做:
PerfLevelSpec[] spec = new PerfLevelSpec [1];
String[] modulePath = new String[] {"threadPoolModule"}
spec[0] = pmiClient.createPerfLevelSpec
(modulePath, PmiConstants.LEVEL_HIGH);
pmiClient.setInstrumentationLevel
(nodeName, serverName, spec, recursiveFlag);
通过 setInstrumentationLevel 所作的修改将立即生效(不需重启服务器)并会持久。
注意,对于一个给定的模块,所有级别不一定都适用。例如,JVM 模块有三个计数器,在级别设置为“低”时所有这些计数器都会被打开。在这种情况下,将其设置为高于“低”的级别将仍然只给出这三个计数器。Max 级别用于在 EJB 模块中开启方法级别(method-level)监控。
装备级别还表示收集数据所引起的性能损耗。PMI 旨在成为一个低损耗框架,所以它可以用于生产监控。我们在实验室中测试时发现了下面的现象:
如果所有模块都设置为“高”:大约 2% 的性能降级
如果所有模块设置为“最高”: 大约 5% 的性能降级
一旦为给定模块设置了装备级别,获取性能数据就只是一个方法调用这么简单了。您可以发送一个或多个 PerfDescriptor 并获取相应的 CpdCollection 对象,如下所示:
// Get all sub-modules recursively
Boolean recursiveFlag = true;
// Get performance data for ONE module
CpdCollection col = pmiClient.get (modulePd[0], recursiveFlag);
// Or, get performance data for ALL of the modules
CpdCollection[] col = pmiClient.gets (modulePd, recursiveFlag);
现在,让我们遍历 CpdCollection 来获取单独的性能数据:
// Get the performance data set from a CpdCollection
CpdData[] perfData = col.dataMembers();
// Get the sub-modules
CpdCollection[] subPerfData = col.subcollections ();
正如前面提到的,PerfDescriptor 识别 CpdData。所以,我们可以从 PerfDescriptor 获取数据名称,如下所示:
perfData[0].getDescriptor().getName();
//e.g. threadPoolModule.threadCreates
或者
perfData[0].getDescriptor().getFullName();
// e.g. root/flow/DefaultServer/threadPoolModule/
threadPoolModule.threadCreates
最后,让我们看看真正的计数器值本身:
// Get the counter value
CpdValue value = perfData[0].getValue ();
// Refer to Step 5 for the definition of returned double value
double numOfThreadsCreated = value.getValue();
long timeInMillis = value.getTime();
timeInMillis 表示和计数器值相关联的时间。
来自 CpdData 的计数器值可能是下面三种类型中的其中之一:
类型 | 描述 | 类 | 方法 |
Numeric | 简单的数字计数器,例如创建的线程 | CpdValue | getValue() — 返回数字计数 |
Statistic | 计数器的平均值,例如平均响应时间 | CpdStat | getValue() — 返回平均数
count() — 返回样本数 |
Load | 时间加权平均值,例如池的大小 | CpdLoad | getValue() — 返回当前值
mean() — 返回时间加权平均值 |
计数器类型可以这样决定,如下所示:
CpdValue value = perfData[0].getValue ();
// Counter type
int counterType = value.getType();
if (counterType == PmiConstants.TYPE_STAT)
{
}
else
if (CounterType == PmiConstants.TYPE_LOAD)
{
}
else
{
// Numeric Type
}
在找到计数器类型之后,您可以调用合适的方法来获取计数器值和它们的组件(如果有的话,如平均数、权数等)。
深入了解 PMI 特性
迄今为止,我们看到了如何使用 PMI API 从 WebSphere Application Server 获取基本的性能数据。现在,让我们深入了解一些 PMI 特性。
每次我们使用 PerfDescriptor 查询 PmiClient 时,都会得到一个新的 CpdCollection 对象。除了每次都处理新的 CpdCollection 对象,我们还可以用新的集合更新现存的集合,如下所示:
// Original collection
CpdCollection orgCol = pmiClient.get (modulePd)
// Updating the original collection with new collection
orgCol.update (pmiClient.get (modulePd));
这等同于建立一个基值然后用一个新的值更新基值。这个模型可以用于创建一个快照视图(这在下一个特性内容中有所解释)。缺省情况下,update 方法将递归更新所有子集合并删除(从基本集合)任何新集合中不存在的集合。缺省行为可以通过使用下面的方法来改变:
update (CpdCollection newCol, Boolean keepOld);
update (CpdCollection newCol, Boolean keepOld, Boolean, recursiveFlag);
还有,CpdCollection 上的 update() 将生成事件(CpdEvent)表明在原来集合中所作的改动,例如新数据和新的子模块等等。通过实现 CpdEventListener 接口,您可以接收所有这些事件。这个特性在使用 PMI 开发 GUI 应用程序时将会特别有用。注意,事件只能发送到本地 JVM 进程的监听器。
应用程序服务器从启动时就保存累计的原始计数器值。客户端中的数据可以复位以捕捉快照视图。
下面是一个示例,它寻找间隔 t2 和 t3 之间处理的请求数。
让我们假定下面的样本数据:
时间 | 服务器值 (处理的请求总数) | 客户机值 (没有任何复位) | 客户机值 (在 t2 复位) |
t0 | 0 | 0 | 0 |
t1 | 100 | 100 | 100 |
t2 | 180 | 180 | 180 复位为 0 |
t3 | 250 | 250 | 70 |
t4 | 300 | 300 | 120 |
从上面的样本数据来看,在间隔 t2 到 t3 之间处理的请求数为 70,t2 和 t4 之间为 120。
这可以通过维护一个基本的 CpdCollection 并按前面特性中所描述的去更新它来达到有计划的计算。例如,要计算 t2 和 t3 之间的请求数,就在 t2 时调用 CpdCollection(或 CpdData)上的 reset() 方法。在 t3 时,从服务器获取数据并更新基本的 CpdCollection。现在,基本集合中的数据将表示从 t2 到 t3 的一个快照。
我们已经看了如何获取性能数据的动态方面(真实的计数器)。静态信息如计数器名、描述、级别等都可以从 PmiDataInfo 类获得。
PmiDataInfo 可以用两种方式获得,如下所示:
从 CpdData 实例获取 PmiDataInfo:
// CpdData[] perfData;
PmiDataInfo staticInfo = perfData[0].getPmiDataInfo ();
staticInfo.getName();
staticInfo.getLevel ();
staticInfo.getComment();
从 PmiModuleConfig 获取 PmiDataInfo:
// Get config for all modules
PmiModuleConfig[] configs = PmiClient.getConfigs();
// Locate a module
String modName = configs[0].getShortName (); // ex: "threadPoolModule"
// List the dataset in the given module config
PmiDataInfo[] staticInfo = configs[0].listAllData ();
因为 PmiClient 使用 RMI/IIOP 连接到 WebSphere AdminServer,所以您需要在防火墙上“戳一个洞”连接到防火墙后面的服务器。或者,您还可以使用性能 servlet 收集数据。
当 WebSphere Application Server 的安全为 ON 时,用户在调用任何 PMI API 之前都需要验证。您可以配置 %WAS_HOME%\properties\sas.client.props 以选择合适的登录源。
WebSphere 资源分析器是一个缺省的 PMI 数据浏览器,在 WebSphere Application Server 4.0 高级版中可以找到。它使用 PMI API 并提供了一个简单的 GUI 用于监控、记录和重放性能数据。
性能 servlet(PerfServlet)是一个防火墙友好的 PMI 数据浏览器,也可以在 WebSphere Application Server 4.0 高级版中找到( %WAS_HOME%\installableApps\perfServletApp.ear )。请在一个节点的任何一个应用程序服务器安装此 EAR 文件,并使用这个 URL 调用它: http://localhost/wasPerfTool/servlet/perfservlet。PerfServlet 提供 XML 格式的性能数据。
WebSphere Statistic 工具是一个使用 PMI 开发的命令行性能监控工具。在调用时,它会列出所有可以监控的模块。它让您可以按照一个给定的刷新间隔一次监控一个模块,如果需要还可以将数据记录在文件中。
这里可以得到源代码。下面的图 4 展示了 wsstat 工具的屏幕截图。
图 4. WebSphere Statistic 工具
WebSphere PMI API 让您从 WebSphere Application Server 搜集性能数据。PMI API 可以用来建立定制工具,在生产环境中监控 WebSphere Application Server。
本文示例源代码或素材下载
Tags:使用 WebSphere Application
编辑录入:爽爽 [复制链接] [打 印]更多精彩
赞助商链接