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 容器线程池模块的完全限定名就可以是:
|
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 4.0 InfoCenter了解不同安全配置的详情。
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。