技巧和模式
简介: 运行时性能监控对于实现和维护性能优异的系统至关重要。本文是系列文章的第 1 部分(共 3 部分),Nicholas Whitehead 将阐述如何有效地对 Java™ 性能执行低级粒度的监控。您生成的数据可以很好的反映系统操作的内在状态,并揭示影响环境稳定性及性能的约束和因素。
简介
当今的许多 Java 应用程序都依赖于一组复杂的分布式依赖关系和移动部件。很多外部因素都可能对应用程序的性能和可用性造成影响。这些影响基本上都无法完全消除或解决,且难以在预生成环境中准确模拟。Stuff happens。但是,您可以创建并维护一个全面的系统来监控应用程序的整个生态系统,从而显著降低这些事件的严重性和持续时间。
本系列文章给出了实现此类系统的一些模式和技巧。模式,以及我将使用的一些术语,都表示泛指。通过结合示例代码和插图,它们将帮助您理解应用程序性能监控的概念。这种理解强调解决方案的必要性,并能帮助您选择商业或开源的解决方案。您可以扩展和定制一个解决方案,或者根据需要将其作为设计解决方案的蓝图。
第 1 部分:
第 2 部分将重点介绍插装 Java 类及资源而无需修改原始源代码的方法。第 3 部分将论述监控 JVM 外部资源的方法,包括主机及其操作系统以及数据库和消息传递系统等远程服务。它还将总结并归纳其他的 APM 问题,如数据管理、数据虚拟化、报告和报警。
回页首
APM 系统:模式和反面模式
为让大家正确入门,应当强调,虽然此处介绍的多数与 Java 相关的内容看上去与应用程序和代码性能分析的流程类似,但其实并非 如此。性能分析是一个极具价值的生产前流程,它可以确认您的 Java 代码是否可扩展、高效、快速和足够出色。但是,根据stuff happens 公理,当您在生产中遇到无法说明的问题时,优秀的开发阶段代码性能分析可能无用武之地。
我的意思是,在生产中实现性能分析的一些方面,并从运行中的应用程序收集一些相同的实时数据及其所有外部依赖关系。该数据由一系列遍及目标的定量测量指标组成,它们为整个系统的健康状况提供细粒度和详细的表示。此外,通过保留这些指标的历史库,您可以捕获准确的基线,以帮助您确认环境仍然健康,或查明特定缺陷的根源和规模。
监控反面模式
完全没有监控资源的应用程序微乎其微,但仍然需要考虑这些反面模式,它们经常出现在运行环境中:
脱节和断开的监控系统:应用程序可以由大型共享数据中心托管,其中,依赖关系由一系列共享资源组成,比如说数据库、存储区网络(SAN)库、消息传递及中间件服务。组织有时高度孤立,各小组只负责管理自己的监控和 APM 系统(请参阅孤立监控的缺陷 侧栏)。没有各依赖关系的整合视图,各组件所有者只能管中窥豹,只见一斑。
图 1 对比了孤立和整合的 APM 系统:
整合 APM 的实现并不排除监控和诊断工具,如 DBA 管理工具集、低级网络分析应用程序和数据中心管理解决方案。这些工具仍然是无价的资源,但如果它们依赖于整合视图的专有性,则难以克服孤立效果的影响。
理想 APM 系统的属性
与刚才讨论的反面模式相反,本系列文章介绍的理想 APM 系统拥有以下属性:
在深入研究此系统的实现细节之前,了解 APM 系统的一些基本概念是有帮助的。
回页首
APM 系统概念
所有 APM 系统都能访问性能数据源 并提供数据收集 和跟踪 实用工具。注意,这些是我自己选择的用于描述一般类别的通用术语。它们并非特定于任何 APM 系统,不同 APM 系统可以使用其他术语表示相同的概念。在本文的其余部分中,我所使用的术语定义如下。
性能数据源
性能数据源(PDS)是性能或可用性数据的来源,这些数据对于反映组件的相对健康状况非常有用。例如,Java Management Extensions (JMX) 服务通常可以提供关于 JVM 健康状况的丰富数据。大多数关系数据库通过 SQL 接口发布性能数据。这两种 PDS 都是直接 源的例子,即可以直接提供性能数据。相反,推断 源测定有意和偶然操作,并且产生性能数据。例如,测试消息可以定期发送,并随后从 Java Message Service (JMS) 服务器中取回,这个往返时间将作为该服务性能的推断测量。
推断源(它的实例被称作综合事务)有时极为有用,因为它们可以通过遍历与实际活动相同的路径来有效测定多个组件或分层调用。综合事务还在监控连续性方面发挥着重要作用,当直接源不能胜任时,它们可以确认系统在相对空闲期的健康状况。
收集和收集器
收集是从 PDS 获取性能或可用性数据的流程。对于直接 PDS,收集器 通常实现一些 API 来访问该数据。要从网络路由器读取统计数据,收集器可以使用简单网络管理协议(Simple Network Management Protocol,SNMP)或 Telnet。对于推断 PDS,收集器用于执行和测定底层操作。
跟踪和跟踪程序
跟踪是收集器向核心 APM 系统交付测量数据的流程。许多商业和开源 APM 系统都提供了一些用于此目的的 API。对于本文中的示例,我实现了一个通用的 Java 跟踪程序接口,将在下节详细讨论。
通常,大多数 APM 系统将跟踪程序提交的数据组织到某种分类的层次结构中。图 2 展示了该数据捕获的一般流程:
图 2 还展示了 APM 系统提供的常用服务:
公共跟踪 API 在 APM 的目标环境中的实现和应用提供了一些一致性。此外,自定义收集器的目的是让开发人员能够专心获取性能数据,而不必担心跟踪的问题。下一节将介绍解决此问题的 APM 跟踪接口。
回页首
ITracer:跟踪程序接口
Java 语言可以很好地充当收集器的实现语言,因为:
但是,有一点需要注意,您的 Java 收集器必须能够与目标 APM 系统提供的跟踪 API 相结合。如果您的 APM 跟踪机制未提供 Java 接口,则它的一些模式将仍然适用。但是,如果目标 PDS 只基于 Java(如 JMX),而应用程序平台并不基于 Java,则需要一个桥接接口(如 IKVM)和一个 Java-to-.NET 编译器(请参阅参考资料)。
当缺少官方标准时,不同 APM 产品提供的跟踪 API 也全然不同。因此,我通过实现一个通用的跟踪 Java 接口(名称为 org.runtimemonitoring.tracing.ITracer
)抽象了此问题。ITracer
接口是针对专用跟踪 API 的一个通用包装器。此技巧将确保源代码库不会因版本或 API 提供程序而有所不同,并且还支持实现包装 API 中不可用的额外功能。本文中的大多数其余示例都实现了ITracer
接口和它所支持的一般底层概念。
图 3 是 org.runtimemonitoring.tracing.ITracer
接口的 UML 类图:
ITracer
接口和工厂类跟踪类别和名称
ITracer
的基本前提是向中央 APM 系统提交一个度量和相关的名称。此活动由 trace
方法实现,该方法因提交的度量而有所不同。各 trace 方法都接受一个String[] name
参数,其中包含复合名称的上下文组件,其结构特定于 APM 系统。复合名称向 APM 系统指示提交的名称空间和实际的指标名称;因此,复合名称中通常至少包括根类别和度量说明。底层ITracer
实现应该知道如何通过传递的String[]
构建复合名称。表 1 演示了复合命名约定的两个示例:
名称结构 | 复合名称 |
---|---|
简单斜杠分隔 | Hosts/SalesDatabaseServer/CPU Utilization/CPU3 |
JMX MBean ObjectName |
com.myco.datacenter.apm:type=Hosts,service=SalesDatabaseServer,group=CPU Utilization,instance=CPU3 |
清单 1 是使用此 API 跟踪调用的简短示例:
|
跟踪程序度量数据类型
在此接口中,度量数据可以是以下类型:
int
long
java.util.Date
String
APM 系统提供商可能支持其他数据类型的收集度量数据。
跟踪程序类型
选定了具体的度量数据类型(如 long
)之后,可以根据 APM 系统支持的类型来选择解释特定值的方式。还需记住,各 APM 实现可以使用不同的术语来表示本质相同的类型,并且ITracer
使用了一些通用的命名规则。
ITracer
中表示的跟踪程序类型:
trace(long value, String[] name)
和 trace(int value, String[] name)
方法将发出时间间隔平均值的跟踪(请参阅 时间间隔 侧栏)。这表示每个提交将被转化为当前时间间隔的聚合值。当新时间间隔开始时,聚合值计数器将重置为零。traceSticky(value long, String[] name)
和 traceSticky(value int, String[] name)
方法发出粘附值跟踪。这表示,与时间间隔平均指标相反,聚合将它们的值保留在时间间隔中。如果现在跟踪值 5,而此后不再执行跟踪直到第二天某个时刻,则该指标将保持为 5,直到提供了新值。traceIncident(String[] name)
调用没有指定任何值,并且隐式只增加一次事件增量。当需要计算多次增量时,除了在循环中多次调用该方法之外,另一种较好的方法是通过traceIncident(int value, String[] name)
方法根据 value
来计算合值。String
传递,并且可将可用类型作为常量定义在接口中。当收集器不知道正在收集的数据的类型或跟踪程序类型时,这是一个非常方便的方法,但是也可以直接将收集值和配置的类型名称传递给跟踪程序。TracerFactory
是一个普通的工厂类,用于根据传递的配置属性创建新 ITracer
实例,或者从缓存中引用已创建的ITracer
。
收集器模式
收集通常有三种可选模式,这影响到应该使用的跟踪程序类型:
GET
和 POST
等)或统一资源标识符(URI)来分类。现在,我已经介绍了性能数据跟踪 API、它的底层数据类型和数据收集的模式。接下来,我将通过一些用例和示例来演示 API 的应用。
回页首
监控 JVM
从 JVM 开始实现性能监控是个明智的选择。首先,我将介绍所有 JVM 共同的性能指标,然后再介绍企业给应用程序中经常使用的一些 JVM 驻留组件。通常,Java 应用程序实例是受底层操作系统支持的进程,因此,JVM 监控的某些方面最好是从主机 OS 的视角来理解,这些内容将在第 3 部分中介绍。
在 Java Platform, Standard Edition 5 (Java SE) 发行之前,能够在运行时有效和可靠收集的内部及标准化 JVM 诊断信息非常有限。现在,java.lang.management
接口提供了一些有用的监控点,该接口是所有兼容 Java SE 5(和更新版本)的 JVM 版本的标准。这些 JVM 的某些实现提供了额外的属性指标,但是它们的访问模式却基本相同。我将重点介绍可以通过 JVM 的 MXBeans 访问的标准模式 — 部署在 VM 内部的 JMX MBeans 公开了一个管理和监控接口(请参阅 参考资料):
ClassLoadingMXBean
:监控类加载系统。CompilationMXBean
:监控编译系统。GarbageCollectionMXBean
:监控 JVM 的垃圾收集器。MemoryMXBean
:监控 JVM 的堆和非堆内存空间。MemoryPoolMXBean
:监控 JVM 分配的内存池。RuntimeMXBean
:监控运行时系统。该 MXBean 提供的有用监控指标很少,但它确实提供了 JVM 的输入参数和启动时间及运行时间,这两者在其他派生指标中都是很有用的。ThreadMXBean
:监控线程系统。JMX 收集器的前提是它将获取一个 MBeanServerConnection
对象,该对象可以读取部署在 JVM 中的 MBeans 的属性,读取目标属性的值,并使用ITracer
API 跟踪它们。对于这种类型的收集,决定部署收集器的位置非常关键。可行的选择包括本地部署 和远程部署。
在本地部署中,收集器和它的调用调度程序部署在目标 JVM 中。随后,JMX 收集器组件将使用 PlatformMBeanServer
(可以通过 JVM 内部的MBeanServerConnection
来连接它)访问 MXBeans。在远程部署中,收集器运行在一个单独的进程中,并使用某种形式的 JMX Remoting 来连接目标 JVM。这可能没有本地部署那么高效,但它不需要在目标系统中部署任何额外的组件。JMX Remoting 不在本文的讨论范围之内,但它的实现方法非常简单:部署一个RMIConnectorServer
或在 JVM 中启用外部连接(请参阅 参考资料)。
示例 JMX 收集器
本文的示例 JMX 收集器(请阅读 下载 一节,获取本文的完整源代码)包含三个单独的方法,可用于获取 MBeanServerConnection
。该收集器可以:
java.lang.management.ManagementFactory.getPlatformMBeanServer()
方法,为本地 JVM 的平台MBeanServer
获取一个MBeanServerConnection
。javax.management.MBeanServerFactory.findMBeanServer(String agentId)
方法,为部署在本地 JVM 平台中的备用MBeanServer
获取一个MBeanServerConnection
。注意,一个 JVM 中可以存在多个MBeanServer
,并且,Java Platform, Enterprise Edition (Java EE) 服务器等较为复杂的系统几乎始终拥有特定于应用服务器的MBeanServer
,它是独立的且有别于平台MBeanServer
(请参阅 交叉注册 MBeans 侧边栏)。javax.management.remote.JMXServiceURL
通过标准 RMI Remoting 获取一个远程MBeanServerConnection
。清单 2 是摘录自 JMXCollectorcollect()
方法的代码段,它显示了 ThreadMXBean
中的收集和线程跟踪活动。点击此处 查看完整清单:
collect()
方法的部分代码,它使用 ThreadMXBean
|
清单 2 中的代码将跟踪 TotalThreadsStarted
和 CurrentThreadCount
的值。由于它是轮询收集器,因此两个跟踪都使用粘附选项。但是,由于TotalThreadsStarted
是一个不断增加的数值,因此最吸引人的地方不是绝对值,而是已创建线程的速率。这样,该跟踪程序将使用DeltaSticky
选项。
图 7 显示了此收集器创建的 APM 指标树:
JMX 收集器的一些方面并未显示在清单 2 中(但是可以在 完整源代码 中看到),比如说调度注册,它将每隔 10 分钟为 collect()
方法创建一个定期回调。
在清单 2 中,不同跟踪程序类型和数据类型的实现方法将由数据源决定。例如:
TotalLoadedClasses
和 UnloadedClassCount
将作为粘附增量被跟踪,因为它们的值始终递增,而且增量在测定类加载活动方面比绝对值更加有用。ThreadCount
变量可增加或减少,因此它将作为粘附类型被跟踪。收集错误
将作为内部事件被跟踪,它将在收集遇到异常时递增。为了追求效率,由于目标 MXBeans 的 JMX ObjectName
在目标 JVM 的生存期不会更改,因此收集器使用 ManagementFactory
常量名来缓存名称。
对于 MXBeans 的两种类型 — GarbageCollector
和 MemoryPool
— 准确的ObjectName
无法预先知晓,但是您可以提供一个通用的模式。在这些情况下,在初次执行收集时,您将对MBeanServerConnection
发起一个查询,并请求与提供模式相匹配的所有 MBeans 的列表。为避免未来在目标 JVM 的生存期执行查询,返回的匹配 MBeanObjectName
将缓存在内存中。
在某些情况下,收集的目标 MBean 属性可能不是纯数值类型。MemoryMXBean
和 MemoryPoolMXBean
就是这种情况。对于这些情况,属性类型是可查询键和值的CompositeData
对象。对于java.lang.management
JVM 管理接口,MXBean 标准采用了 JMXOpen Types 模型,在该模型中,所有属性都是语言无关的类型,如java.lang.Boolean
和 java.lang.Integer
。或者,对于 javax.management.openmbean.CompositeType
等复杂类型,这些类型可以被分解为相同简单类型的键/值对。简单类型的完整列表枚举在静态javax.management.openmbean.OpenType.ALLOWED_CLASSNAMES
字段中。该模型支持一个类型独立层,使 JMX 客户机不用依赖于非标准的类,并且还可以支持非 Java 客户机,因为底层类型相对比较简单。有关 JMX Open Types 的更多信息,请参阅参考资料。
对于目标 MBean 属性是非标准复杂类型的情况,您需要确保定义该类型的类在收集器的类路径中。并且,您必须实现一些自定义代码来呈现检索到的复杂对象中的有用数据。
如果获取了单个连接并为所有收集保留了该连接,则需要通过错误检测和修复来创建一个新连接,以防止该连接出现故障。某些收集 API 提供断开监控程序,可以提示收集器关闭、消除和创建新连接。如果收集器尝试连接到由于维护而停机或由于其他原因而无法访问的 PDS,则收集器应该以合适的频率轮询并重新连接。跟踪连接的运行时间还可用于在检测到关机时减少收集的频率。这可以减少已超负荷运行了一段时间的目标 JVM 的开销。
这些示例中未实现的两个额外技巧可以改进 JMX 收集器的效率,并减少它在目标 JVM 中运行所需的开销。第一个技巧适用于从一个 MBean 中查询多个属性的情况。借助getAttributes(ObjectName name, String[] attributes)
,您可以在一个调用中请求多个属性,而不必使用getAttribute(ObjectName name, String attribute)
一次请求一个属性。这种差异在本地收集中可以忽略,但是在远程收集中却可以显著减少资源的使用,因为它可以减少网络调用的数量。第二个技巧是使用监控收集模式代替轮询模式,从而进一步减少 JMX 公开内存池的轮询开销。MemoryPoolMXBean
支持建立一个使用率阀值,超过该阀值时将触发向监控程序发送一个通知,而监控程序将跟踪该值。当内存使用率增加时,使用率阀值可以相应地增加。这种方法是缺陷是,如果使用率阀值没有微小的增量,则一些粒度级的数据可能会丢失,并且阀值下方的内存使用率模式将变为不可见。
最后一个未实现的技巧是测定运行时间和垃圾收集总运行时间的范围,并实现一些简单的算法来计算垃圾收集器处于活动状态的时间在已运行时间中的百分比。这是一个有用的指标,因为一些垃圾收集器(当前)是大多数应用程序必须要面对的问题。由于某些收集(分别执行了一段时间)是期望执行的,因此运行垃圾收集占用的时间可以更加清楚地反映 JVM 的内存健康状况。根据经验(因应用程序而大不相同),占用任何 15 分钟时间段内的 10% 以上则表示存在潜在问题。
收集器的外部配置
为便于演示收集流程,本文介绍的 JMX 收集器经过了适当简化,但它仅限于硬编码的收集方式。理想情况下,收集器将实现数据访问方式,而外部提供的配置将提供内容。这种设计使收集器更具实用性,且易于重用。对于最高级别的重用,外部配置的收集器应该支持这些配置点:
清单 3 演示了 JMX 收集器的外部配置:
|
注意,TargetAttribute
元素包含一个名为 type
的属性,它表示智能类型跟踪程序的参数化变量。SINT
类型表示粘附int
,SDINT
类型表示增量粘附int
。
回页首
通过 JMX 监控应用程序资源
目前为止,我已经讨论了通过 JMX 监控惟一标准的 JVM 资源。但是,许多应用程序架构,如 Java EE,可以通过 JMX 公开重要的特定于应用程序的指标(这取决于供应商)。一个典型的例子是DataSource
利用率。DataSource
是一个用于将连接池化到外部资源(通常为数据库)的服务,这限制了并发连接的数量,以保护资源不受恶意应用程序的占用。监控数据源是整个监控计划中的关键环节。由于 JMX 抽象层,该流程与之前介绍的类似。
下面是来自 JBoss 4.2 应用服务器实例的典型数据源指标:
现在,收集器将使用批属性检索,并在一个调用中获取所有属性。惟一需要注意的是,您需要查询返回的数据,以接通不同的数据和跟踪程序类型。DataSource
指标在没有活动时也是不会变化的,因此,要使数值变化,您需要生成一些负载。清单 4 显示 DataSource 收集器的collect()
方法:
|
图 8 显示了 DataSource 收集器的相应指标树:
回页首
监控 JVM 中的组件
本节介绍的技巧可用于监控应用程序组件、服务、类和方法。相关的主要区域如下:
使用 Java SE 5(和更新版本)ThreadMXBean
的一些实现提供的指标,还可以收集以下指标:
WAITING
或TIMED_WAITING
等待状态并暂停另一个线程的活动时将发生等待事件。 BLOCKED
状态的线程的实例数量和总占用时间。当线程等待监控锁进入或重新进入同步阻塞时会发生阻塞事件。还可以使用备选工具集和本机接口来确定这些指标和其他指标,但这通常涉及某种级别的开销,从而造成不必要的生产运行时监控。已经说过,指标本身,甚至在收集时,是低级的。它们的作用也许仅限于分析趋势,并且很难与无法通过其他手段确定的因果效应相关联。
所有上述指标都可以通过插装类和方法的流程来收集,以便于收集和跟踪目标 APM 系统的性能数据。可以采用各种技巧来直接插装 Java 类,或者通过它们来间接计算性能指标:
在本文的第 1 部分中,我只讨论基于源代码的插装;您将在第 2 部分中了解更多关于截取、字节码插装和类包装的信息。(从拓扑学的角度来说,截取、字节码插装和类包装的本质完全相同,但它们实现结果的操作有稍微不同的含义)。
异步插装
异步插装是类插装中的基本问题。上一节讨论了轮询性能数据的概念。如果轮询完成得足够好,则它应该不会对核心应用程序性能或开销造成影响。相反,插装应用程序代码本身会直接修改和影响核心代码的执行。任何插装的目标都必须是无论如何,不产生危害。开销损失必须尽可能接近忽略不计。事实上,消除测量本身中的极细微的损失是不可能的,但是,在获取性能数据之后,保持其余跟踪进程异步是非常重要的。可以采用若干种模式来实现异步跟踪。图 9 演示了异步跟踪的实现方法概览:
图 9 演示了一个简单的插装截取程序,它通过捕获调用的起始时间来测量它的运行时间,然后将测量数据(运行时间和指标复合名称)分发给处理队列。然后,线程池读取该队列,获取测量数据并完成跟踪流程。
回页首
源代码中的 Java 类插装
本节将讨论如何实现源代码级插装,并将提供一些最佳实践和示例代码。文章还介绍了一些新的跟踪结构,我将在源代码插装的上下文中阐明它们的操作和它们的插装模式。
虽然其他选择已经流行,但源代码插装在某些实例中是无法避免的;在某些情况下,它是惟一的解决方案。借助一些明智的预防措施,它可以实现良好的效果。需要考虑的事项包括:
上下文跟踪
上下文跟踪受具体的应用程序影响极大,但是可以考虑一个经过简化的例子:含有 processPayroll(long clientId)
方法的 payroll-processing 类。当被调用时,该方法计算并存储各客户员工的薪水。您可以通过各种方法插装该方法,但是,执行中的底层模式清楚表明,调用时间的增加与员工的数量不成比例。因此,研究processPayroll
的运行时间趋势没有上下文可供参考,除非您知道程序每次处理的员工数量。简单来讲,对于特定的时间段,processPayroll
平均耗时x 毫秒。无法确定这个值反映的性能是好还是坏,因为您不知道它处理的员工数量是 1 还是 150,而两种情况反映的性能差别巨大。清单 5 在代码中显示了这个简化的概念:
|
此处的主要挑战是,根据大多数插装技巧,processPayroll()
方法中的任何东西都是不可触及的。因此,虽然能够插装 processPayroll
甚至 processEmployee
,但是却无法跟踪员工的数量,从而不能为方法的性能数据提供上下文。清单 6 显示了一个拙劣的硬编码示例(且有点效率不高),它将捕获上面提到的上下文数据:
|
清单 6 中的关键部分是 tracer.lookupRange
调用。Ranges 是指定的收集,它由数值范围限制键控,并且拥有一个表示数值范围名称的String
值。不再跟踪薪水处理的简单无格式运行时间,清单 6 将员工计数划分为范围,有效分隔运行时间并根据基本类似的员工计数将它们分组。图 10 显示了 APM 系统生成的指标树:
图 11 演示了根据员工计数划分的薪水处理运行的时间,它揭示了员工数量和运行时间之间的相互关系:
跟踪程序配置属性允许在属性文件中包括 URL,并能在其中定义范围和阀值(我将简单介绍一下阀值)。属性将在跟踪程序的构造时间被读取,并为 tracer.lookupRange
实现提供后台数据。清单 7 显示了Payroll Processing
范围的示例配置。我选择使用java.util.Properties
的 XML 表示,因为它更能兼容奇怪的字符。
|
注入外部定义的范围可以使您的应用程序不必频繁更新源代码,这受益于预期的调整和服务水平协议(SLA)在业务方面的变更。当范围和阀值更改生效之后,您只需更新外部文件,而不是应用程序本身。
回页首
跟踪阀值和 SLA
外部可配置上下文跟踪的灵活性支持以更加准确和粒度化的方式来定义和测量性能阀值。范围 定义一系列数值区间,可以在其中对测量数据进行分类,而阀值 是对范围的进一步分类,它根据测量数据的确定范围对获取的测量数据进行分类。在分析收集的性能数据时,一个常见的需求是确定和报告执行是 “成功” 还是 “失败”(因为它们未在指定时间发生)。这些数据的总和可以作为关于系统运行健康状况和性能的通用成绩单,或者作为某种形式的 SLA 遵从性评价。
使用薪水处理系统示例,考虑一个内部服务级目标,它将薪水的执行时间(在定义的员工数范围之内)定义为 Ok
、Warn
和Critical
3 个区间。生成阀值计数的流程从概念上来说非常简单。您只需为跟踪程序提供您认为是各类别各区间的上限运行时间的值,并引导跟踪程序为分类的运行时间发起一个tracer.traceIncident
,然后 — 为简化报告 — 提供一个总数。表 2 显示了一些经过设计的 SLA 运行时间:
员工数 | Ok (ms) | Warn (ms) | Critical (ms) |
---|---|---|---|
1-10 | 280 | 400 | >400 |
11-50 | 850 | 1200 | >1200 |
51-80 | 900 | 1100 | >1100 |
81-120 | 1100 | 1500 | >1500 |
121-180 | 1400 | 2000 | >2000 |
181+ | 2000 | 3000 | >3000 |
ITracer
API 使用与范围中相同的 XML(属性)文件中定义的值实现了阀值报告。范围和阀值定义在两个方面稍有不同。首先,阀值定义的关键值是一个正则表达式。当ITracer
在跟踪一个数值时,它会检查阀值正则表达式是否匹配被跟踪指标的复合名称。如果匹配,则阀值会将测量数据分类为Ok
、Warn
或Critical
,并为跟踪附加一个额外的 tracer.traceIncident
。其次,由于阀值只定义了两个值(根据定义,Critical
值大于warn
值),因此配置只由两个数值组成。清单 8 显示了之前介绍的薪水处理 SLA 的阀值配置:
|
图 12 显示添加了阀值指标的薪水处理的指标树:
图 13 演示了哪些收集的数据可以表示在饼形图中:
确保查找上下文和阀值分类的效率和速度非常重要,因为它们在完成实际工作的线程中执行。在 ITracer
实现中,所有指标名称在第一次被跟踪程序发现时,将存储在(线程安全)为具备和不具备阀值的指标指定的映射中。当特定指标的跟踪事件发生后,阀值确定过程占用的时间是一个Map
查找时间,它的速度通常足够快。如果阀值条目或指标名称的数量非常大,则一种合理的解决方案是推迟阀值确定,并在异步跟踪线程池中处理它们。
回页首
第 1 部分结束语
本系列文章的第 1 部分介绍了一些监控反面模式和一些 APM 系统需要的属性。我总结了一些通用性能数据收集模式,并介绍了 ITracer
接口,我将在本系列文章的其余部分继续使用它。我已经演示了监控 JVM 健康状况的技巧,以及如何通过 JMX 获取通用性能数据。最后,我总结了各种实现高效和防代码更改的源代码级插装方法(用于监控原始性能统计数据和上下文派生统计数据),以及如何使用这些统计数据生成关于应用程序 SLA 的报告。第 2 部分将探究插装 Java 系统而无需修改应用程序源代码的技巧,具体方法是使用截取、类包装和动态字节码插装。
回页首
下载
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
本文的示例代码 | j-rtm1.zip | 316KB | HTTP |
关于下载方法的信息