首页 >> 大全

Java 运行时监控,第 1 部分: Java 系统运行时性能和可用性监控

2023-12-28 大全 30 作者:考证青年

简介

当今的许多 Java 应用程序都依赖于一组复杂的分布式依赖关系和移动部件。很多外部因素都可能对应用程序的性能和可用性造成影响。这些影响基本上都无法完全消除或解决,且难以在预生成环境中准确模拟。Stuff 。但是,您可以创建并维护一个全面的系统来监控应用程序的整个生态系统,从而显著降低这些事件的严重性和持续时间。

本系列文章给出了实现此类系统的一些模式和技巧。模式,以及我将使用的一些术语,都表示泛指。通过结合示例代码和插图,它们将帮助您理解应用程序性能监控的概念。这种理解强调解决方案的必要性,并能帮助您选择商业或开源的解决方案。您可以扩展和定制一个解决方案,或者根据需要将其作为设计解决方案的蓝图。

第 1 部分:

第 2 部分将重点介绍插装 Java 类及资源而无需修改原始源代码的方法。第 3 部分将论述监控 JVM 外部资源的方法,包括主机及其操作系统以及数据库和消息传递系统等远程服务。它还将总结并归纳其他的 APM 问题,如数据管理、数据虚拟化、报告和报警。

APM 系统:模式和反面模式

为让大家正确入门,应当强调,虽然此处介绍的多数与 Java 相关的内容看上去与应用程序和代码性能分析的流程类似,但其实并非如此。性能分析是一个极具价值的生产前流程,它可以确认您的 Java 代码是否可扩展、高效、快速和足够出色。但是,根据stuff 公理,当您在生产中遇到无法说明的问题时,优秀的开发阶段代码性能分析可能无用武之地。

我的意思是,在生产中实现性能分析的一些方面,并从运行中的应用程序收集一些相同的实时数据及其所有外部依赖关系。该数据由一系列遍及目标的定量测量指标组成,它们为整个系统的健康状况提供细粒度和详细的表示。此外,通过保留这些指标的历史库,您可以捕获准确的基线,以帮助您确认环境仍然健康,或查明特定缺陷的根源和规模。

监控反面模式

完全没有监控资源的应用程序微乎其微,但仍然需要考虑这些反面模式,它们经常出现在运行环境中:

孤立监控的缺陷

当系统视图无法反映整体情况时,便会出现孤立监控。最为复杂和难以诊断的问题通常涉及多个参与和相关组件。请考虑一个简单的例子,托管在 Java 应用服务器上的应用程序(所有者未知)实现了一个有故障的 JDBC 连接池类(泄漏连接)。

当应用程序所有者查看管理接口时,他们声称自己的服务器与数据库保持了 100 个连接。相反,数据库管理员(DBA)查看数据库管理控制台却能看到相同的主机实际维持了 120 个连接,并且其数量正在迅速增加。在经过整合的APM 系统中,创建一个显示这两种指标的曲线图应该说是微不足道的。当这两个数字彼此背离时,看到该图的任何人都可以立即清楚地看到真实数字,并能准确判断出问题所在。

整合 APM 的实现并不排除监控和诊断工具,如 DBA 管理工具集、低级网络分析应用程序和数据中心管理解决方案。这些工具仍然是无价的资源,但如果它们依赖于整合视图的专有性,则难以克服孤立效果的影响。

理想 APM 系统的属性

与刚才讨论的反面模式相反,本系列文章介绍的理想 APM 系统拥有以下属性:

在深入研究此系统的实现细节之前,了解 APM 系统的一些基本概念是有帮助的。

APM 系统概念

所有 APM 系统都能访问性能数据源并提供数据收集和跟踪实用工具。注意,这些是我自己选择的用于描述一般类别的通用术语。它们并非特定于任何 APM 系统,不同 APM 系统可以使用其他术语表示相同的概念。在本文的其余部分中,我所使用的术语定义如下。

性能数据源

性能数据源(PDS)是性能或可用性数据的来源,这些数据对于反映组件的相对健康状况非常有用。例如,Java (JMX) 服务通常可以提供关于 JVM 健康状况的丰富数据。大多数关系数据库通过 SQL 接口发布性能数据。这两种 PDS 都是直接源的例子,即可以直接提供性能数据。相反,推断源测定有意和偶然操作,并且产生性能数据。例如,测试消息可以定期发送,并随后从 Java (JMS) 服务器中取回,这个往返时间将作为该服务性能的推断测量。

推断源(它的实例被称作综合事务)有时极为有用,因为它们可以通过遍历与实际活动相同的路径来有效测定多个组件或分层调用。综合事务还在监控连续性方面发挥着重要作用,当直接源不能胜任时,它们可以确认系统在相对空闲期的健康状况。

收集和收集器

收集是从 PDS 获取性能或可用性数据的流程。对于直接 PDS,收集器通常实现一些 API 来访问该数据。要从网络路由器读取统计数据,收集器可以使用简单网络管理协议( ,SNMP)或 。对于推断 PDS,收集器用于执行和测定底层操作。

跟踪和跟踪程序

跟踪是收集器向核心 APM 系统交付测量数据的流程。许多商业和开源 APM 系统都提供了一些用于此目的的 API。对于本文中的示例,我实现了一个通用的 Java 跟踪程序接口,将在下节详细讨论。

通常,大多数 APM 系统将跟踪程序提交的数据组织到某种分类的层次结构中。图 2 展示了该数据捕获的一般流程:

图 2. 收集、跟踪和 APM 系统

图 2 还展示了 APM 系统提供的常用服务:

公共跟踪 API 在 APM 的目标环境中的实现和应用提供了一些一致性。此外,自定义收集器的目的是让开发人员能够专心获取性能数据,而不必担心跟踪的问题。下一节将介绍解决此问题的 APM 跟踪接口。

:跟踪程序接口

Java 语言可以很好地充当收集器的实现语言,因为:

但是,有一点需要注意,您的 Java 收集器必须能够与目标 APM 系统提供的跟踪 API 相结合。如果您的 APM 跟踪机制未提供 Java 接口,则它的一些模式将仍然适用。但是,如果目标 PDS 只基于 Java(如 JMX),而应用程序平台并不基于 Java,则需要一个桥接接口(如 IKVM)和一个 Java-to-.NET 编译器(请参阅参考资料)。

当缺少官方标准时,不同 APM 产品提供的跟踪 API 也全然不同。因此,我通过实现一个通用的跟踪 Java 接口(名称为org...)抽象了此问题。接口是针对专用跟踪 API 的一个通用包装器。此技巧将确保源代码库不会因版本或 API 提供程序而有所不同,并且还支持实现包装 API 中不可用的额外功能。本文中的大多数其余示例都实现了接口和它所支持的一般底层概念。

图 3 是org...接口的 UML 类图:

图 3.接口和工厂类

跟踪类别和名称

的基本前提是向中央 APM 系统提交一个度量和相关的名称。此活动由trace方法实现,该方法因提交的度量而有所不同。各 trace 方法都接受一个[] name参数,其中包含复合名称的上下文组件,其结构特定于 APM 系统。复合名称向 APM 系统指示提交的名称空间和实际的指标名称;因此,复合名称中通常至少包括根类别和度量说明。底层实现应该知道如何通过传递的[]构建复合名称。表 1 演示了复合命名约定的两个示例:

表 1. 示例复合名称 名称结构 复合名称

简单斜杠分隔

Hosts//CPU /CPU3

JMX

com.myco..apm:type=Hosts,=,group=CPU ,=CPU3

清单 1 是使用此 API 跟踪调用的简短示例:

清单 1. 跟踪 API 调用示例

ITracer simpleTracer = TracerFactory.getInstance(sprops);
ITracer jmxTracer = TracerFactory.getInstance(jprops);
.
.
simpleTracer.trace(37, "Hosts", "SalesDatabaseServer","CPU Utilization", "CPU3", "Current Utilization %");
jmxTracer.trace(37, "com.myco.datacenter.apm", "type=Hosts", "service=SalesDatabaseServer", "group=CPU Utilization", "instance=CPU3", "Current Utilization %");
);

跟踪程序度量数据类型

在此接口中,度量数据可以是以下类型:

APM 系统提供商可能支持其他数据类型的收集度量数据。

跟踪程序类型

选定了具体的度量数据类型(如long)之后,可以根据 APM 系统支持的类型来选择解释特定值的方式。还需记住,各 APM 实现可以使用不同的术语来表示本质相同的类型,并且使用了一些通用的命名规则。

时间间隔

讨论跟踪程序类型需要了解时间间隔的概念。请考虑这样一个概念:进程正在收集操作已经运行的时间并将它发送给 APM 系统。您每分钟都可以看到成百上千的调用。传输并存储每个度量的细节并不是切实有效的方法,而且也不应该在任何性能报告或图表中反应每一个度量,因为即便非常罕见的调用也可以歪曲整个信息表示。同时,由于长时期过度捕获,您将丧失粒度化的概念,因为这段时间内的合法峰值可能相当多。

解决此问题的一种模式是选取一个时间时隔来表示您希望聚合的最低粒度。1 个小时可能太长,而 200 毫秒又太短,因此您可以设定 30 秒的间隔。现在,您仍然可以对假设操作的每次调用都调用 trace,但是,保留在各时间间隔末尾的数据是:

这是过度聚合度量和存储所有单独度量的一种有效的折衷方法。

中表示的跟踪程序类型:

是一个普通的工厂类,用于根据传递的配置属性创建新实例,或者从缓存中引用已创建的。

收集器模式

收集通常有三种可选模式,这影响到应该使用的跟踪程序类型:

由于您可以假定截取收集器能 “看到” 每一个事件,因此实现的跟踪程序通常为平均时间间隔类型。因此,如果时间间隔到期且没有活动发生,则该时间间隔的聚合值将为零,而与之前时间间隔中的活动无关。图 6 演示了此模式: 图 6. 截取收集模式

现在,我已经介绍了性能数据跟踪 API、它的底层数据类型和数据收集的模式。接下来,我将通过一些用例和示例来演示 API 的应用。

监控 JVM

从 JVM 开始实现性能监控是个明智的选择。首先,我将介绍所有 JVM 共同的性能指标,然后再介绍企业给应用程序中经常使用的一些 JVM 驻留组件。通常,Java 应用程序实例是受底层操作系统支持的进程,因此,JVM 监控的某些方面最好是从主机 OS 的视角来理解,这些内容将在第 3 部分中介绍。

在 Java , 5 (Java SE) 发行之前,能够在运行时有效和可靠收集的内部及标准化 JVM 诊断信息非常有限。现在,java.lang.接口提供了一些有用的监控点,该接口是所有兼容 Java SE 5(和更新版本)的 JVM 版本的标准。这些 JVM 的某些实现提供了额外的属性指标,但是它们的访问模式却基本相同。我将重点介绍可以通过 JVM 的 访问的标准模式 — 部署在 VM 内部的 JMX 公开了一个管理和监控接口(请参阅参考资料):

JMX 收集器的前提是它将获取一个n对象,该对象可以读取部署在 JVM 中的 的属性,读取目标属性的值,并使用 跟踪它们。对于这种类型的收集,决定部署收集器的位置非常关键。可行的选择包括本地部署和远程部署。

权限问题

本地和远程部署都可能会受到各种配置权限的限制,这些权限会阻止收集器访问 JVM 数据。这些问题大多都有相应的解决方法,但是它们的多变性使本文无法详细介绍这方面的内容。

在本地部署中,收集器和它的调用调度程序部署在目标 JVM 中。随后,JMX 收集器组件将使用(可以通过 JVM 内部的n来连接它)访问 。在远程部署中,收集器运行在一个单独的进程中,并使用某种形式的 JMX 来连接目标 JVM。这可能没有本地部署那么高效,但它不需要在目标系统中部署任何额外的组件。JMX 不在本文的讨论范围之内,但它的实现方法非常简单:部署一个或在 JVM 中启用外部连接(请参阅参考资料)。

示例 JMX 收集器

本文的示例 JMX 收集器(请阅读下载一节,获取本文的完整源代码)包含三个单独的方法,可用于获取n。该收集器可以:

清单 2 是摘录自 ()方法的代码段,它显示了中的收集和线程跟踪活动。点击此处查看完整清单:

清单 2. 示例 JMX 收集器的()方法的部分代码,它使用

.
.
objectNameCache.put(THREAD_MXBEAN_NAME, new ObjectName(THREAD_MXBEAN_NAME));
.
.
public void collect() {CompositeData compositeData = null;String type = null;try {log("Starting JMX Collection");long start = System.currentTimeMillis();ObjectName on = null;
.
.// Thread Monitoringon = objectNameCache.get(THREAD_MXBEAN_NAME);tracer.traceDeltaSticky((Long)jmxServer.getAttribute(on,"TotalStartedThreadCount"), hostName, "JMX", on.getKeyProperty("type"), "StartedThreadRate");tracer.traceSticky((Integer)jmxServer.getAttribute(on, "ThreadCount"), hostName, "JMX", on.getKeyProperty("type"), "CurrentThreadCount");
.
.// Donelong elapsed = System.currentTimeMillis()-start;tracer.trace(elapsed, hostName, "JMX", "JMX Collector", "Collection", "Last Elapsed Time");tracer.trace(new Date(), hostName, "JMX", "JMX Collector", "Collection", "Last Collection");         log("Completed JMX Collection in ", elapsed, " ms.");         } catch (Exception e) {log("Failed:" + e);tracer.traceIncident(hostName, "JMX", "JMX Collector", "Collection", "Collection Errors");}
}

清单 2 中的代码将跟踪和的值。由于它是轮询收集器,因此两个跟踪都使用粘附选项。但是,由于是一个不断增加的数值,因此最吸引人的地方不是绝对值,而是已创建线程的速率。这样,该跟踪程序将使用选项。

图 7 显示了此收集器创建的 APM 指标树:

图 7. JMX 收集器 APM 指标树

JMX 收集器的一些方面并未显示在清单 2 中(但是可以在完整源代码中看到),比如说调度注册,它将每隔 10 分钟为()方法创建一个定期回调。

在清单 2 中,不同跟踪程序类型和数据类型的实现方法将由数据源决定。例如:

为了追求效率,由于目标 的 在目标 JVM 的生存期不会更改,因此收集器使用常量名来缓存名称。

对于 的两种类型 —和— 准确的无法预先知晓,但是您可以提供一个通用的模式。在这些情况下,在初次执行收集时,您将对n发起一个查询,并请求与提供模式相匹配的所有 的列表。为避免未来在目标 JVM 的生存期执行查询,返回的匹配 将缓存在内存中。

在某些情况下,收集的目标 MBean 属性可能不是纯数值类型。和就是这种情况。对于这些情况,属性类型是可查询键和值的对象。对于java.lang. 管理接口, 标准采用了 Types模型,在该模型中,所有属性都是语言无关的类型,如java.lang.和java.lang.。或者,对于javax...等复杂类型,这些类型可以被分解为相同简单类型的键/值对。简单类型的完整列表枚举在静态javax....字段中。该模型支持一个类型独立层,使 JMX 客户机不用依赖于非标准的类,并且还可以支持非 Java 客户机,因为底层类型相对比较简单。有关 JMX Open Types 的更多信息,请参阅参考资料。

对于目标 MBean 属性是非标准复杂类型的情况,您需要确保定义该类型的类在收集器的类路径中。并且,您必须实现一些自定义代码来呈现检索到的复杂对象中的有用数据。

如果获取了单个连接并为所有收集保留了该连接,则需要通过错误检测和修复来创建一个新连接,以防止该连接出现故障。某些收集 API 提供断开监控程序,可以提示收集器关闭、消除和创建新连接。如果收集器尝试连接到由于维护而停机或由于其他原因而无法访问的 PDS,则收集器应该以合适的频率轮询并重新连接。跟踪连接的运行时间还可用于在检测到关机时减少收集的频率。这可以减少已超负荷运行了一段时间的目标 JVM 的开销。

这些示例中未实现的两个额外技巧可以改进 JMX 收集器的效率,并减少它在目标 JVM 中运行所需的开销。第一个技巧适用于从一个 MBean 中查询多个属性的情况。借助( name, [] ),您可以在一个调用中请求多个属性,而不必使用( name, )一次请求一个属性。这种差异在本地收集中可以忽略,但是在远程收集中却可以显著减少资源的使用,因为它可以减少网络调用的数量。第二个技巧是使用监控收集模式代替轮询模式,从而进一步减少 JMX 公开内存池的轮询开销。支持建立一个使用率阀值,超过该阀值时将触发向监控程序发送一个通知,而监控程序将跟踪该值。当内存使用率增加时,使用率阀值可以相应地增加。这种方法是缺陷是,如果使用率阀值没有微小的增量,则一些粒度级的数据可能会丢失,并且阀值下方的内存使用率模式将变为不可见。

最后一个未实现的技巧是测定运行时间和垃圾收集总运行时间的范围,并实现一些简单的算法来计算垃圾收集器处于活动状态的时间在已运行时间中的百分比。这是一个有用的指标,因为一些垃圾收集器(当前)是大多数应用程序必须要面对的问题。由于某些收集(分别执行了一段时间)是期望执行的,因此运行垃圾收集占用的时间可以更加清楚地反映 JVM 的内存健康状况。根据经验(因应用程序而大不相同),占用任何 15 分钟时间段内的 10% 以上则表示存在潜在问题。

收集器的外部配置

为便于演示收集流程,本文介绍的 JMX 收集器经过了适当简化,但它仅限于硬编码的收集方式。理想情况下,收集器将实现数据访问方式,而外部提供的配置将提供内容。这种设计使收集器更具实用性,且易于重用。对于最高级别的重用,外部配置的收集器应该支持这些配置点:

清单 3 演示了 JMX 收集器的外部配置:

清单 3. JMX 收集器的外部配置示例


collectors.jmx.RemoteRMIMBeanServerConnectionFactoryjmx.rmi.url=service:jmx:rmi://127.0.0.1/jndi/rmi://127.0.0.1:1090/jmxconnectorAppServer3.myco.org,JMX10000      

注意,元素包含一个名为type的属性,它表示智能类型跟踪程序的参数化变量。SINT类型表示粘附int,SDINT类型表示增量粘附int。

通过 JMX 监控应用程序资源

目前为止,我已经讨论了通过 JMX 监控惟一标准的 JVM 资源。但是,许多应用程序架构,如 Java EE,可以通过 JMX 公开重要的特定于应用程序的指标(这取决于供应商)。一个典型的例子是利用率。是一个用于将连接池化到外部资源(通常为数据库)的服务,这限制了并发连接的数量,以保护资源不受恶意应用程序的占用。监控数据源是整个监控计划中的关键环节。由于 JMX 抽象层,该流程与之前介绍的类似。

下面是来自 JBoss 4.2 应用服务器实例的典型数据源指标:

现在,收集器将使用批属性检索,并在一个调用中获取所有属性。惟一需要注意的是,您需要查询返回的数据,以接通不同的数据和跟踪程序类型。指标在没有活动时也是不会变化的,因此,要使数值变化,您需要生成一些负载。清单 4 显示 收集器的()方法:

清单 4. 收集器

public void collect() {try {log("Starting DataSource Collection");long start = System.currentTimeMillis();ObjectName on = objectNameCache.get("DS_OBJ_NAME");AttributeList attributes  = jmxServer.getAttributes(on, new String[]{"AvailableConnectionCount", "MaxConnectionsInUseCount","InUseConnectionCount","ConnectionCount","ConnectionCreatedCount","ConnectionDestroyedCount"});for(Attribute attribute: (List)attributes) {if(attribute.getName().equals("ConnectionCreatedCount") || attribute.getName().equals("ConnectionDestroyedCount")) {tracer.traceDeltaSticky((Integer)attribute.getValue(), hostName, "DataSource", on.getKeyProperty("name"), attribute.getName());} else {if(attribute.getValue() instanceof Long) {tracer.traceSticky((Long)attribute.getValue(), hostName, "DataSource", on.getKeyProperty("name"), attribute.getName());} else {tracer.traceSticky((Integer)attribute.getValue(), hostName, "DataSource",on.getKeyProperty("name"), attribute.getName());}}}// Donelong elapsed = System.currentTimeMillis()-start;tracer.trace(elapsed, hostName, "DataSource", "DataSource Collector", "Collection", "Last Elapsed Time");tracer.trace(new Date(), hostName, "DataSource", "DataSource Collector", "Collection", "Last Collection");         log("Completed DataSource Collection in ", elapsed, " ms.");         } catch (Exception e) {log("Failed:" + e);tracer.traceIncident(hostName, "DataSource", "DataSource Collector", "Collection", "Collection Errors");}      
}

图 8 显示了 收集器的相应指标树:

图 8. 收集器指标树

监控 JVM 中的组件

交叉注册

在许多情况下,可以将目标 注册到相同 JVM 的不同中。例如,java.lang 注册在平台代理(也称作 )中,而 JBoss 服务器中的 位于jboss 中。在远程监控实现中,这种情况有时会增加额外的开销和配置复杂度,因为每个都需要两个远程连接。此外,为远程连接公开平台也会带来额外的开销。在这些情况下,通常采用交叉注册 的方式,这样所有的目标 都可以通过相同的接口来监控。请查阅本文的源代码,获取名称为 map--.bsh 的示例 bean-shell 脚本。如果您将此脚本部署到 JBoss 服务器中,它将在 中交叉注册平台的 。

本节介绍的技巧可用于监控应用程序组件、服务、类和方法。相关的主要区域如下:

使用 Java SE 5(和更新版本)的一些实现提供的指标,还可以收集以下指标:

还可以使用备选工具集和本机接口来确定这些指标和其他指标,但这通常涉及某种级别的开销,从而造成不必要的生产运行时监控。已经说过,指标本身,甚至在收集时,是低级的。它们的作用也许仅限于分析趋势,并且很难与无法通过其他手段确定的因果效应相关联。

所有上述指标都可以通过插装类和方法的流程来收集,以便于收集和跟踪目标 APM 系统的性能数据。可以采用各种技巧来直接插装 Java 类,或者通过它们来间接计算性能指标:

字节码插装:该流程将字节码注入到应用程序类中。注入的字节码将添加性能数据收集插装,该插装被作为新类的一部分调用。这个流程有时极为有效,因为插装是完全经过编译的字节码,并且代码的执行路径以最细化的方式扩展,同时仍然能够收集数据。它的另一个优点是无需修改初始源代码,并且其对环境的配置更改也可能最少。此外,通用模式和字节码注入技巧允许对源代码不可用的类和库进行插装,许多第三方类属于这种情况。 类包装:该流程使用另一个类来包装或替换目标类,前者实现了相同功能,同时也包含了插装。

在本文的第 1 部分中,我只讨论基于源代码的插装;您将在第 2 部分中了解更多关于截取、字节码插装和类包装的信息。(从拓扑学的角度来说,截取、字节码插装和类包装的本质完全相同,但它们实现结果的操作有稍微不同的含义)。

异步插装

异步插装是类插装中的基本问题。上一节讨论了轮询性能数据的概念。如果轮询完成得足够好,则它应该不会对核心应用程序性能或开销造成影响。相反,插装应用程序代码本身会直接修改和影响核心代码的执行。任何插装的目标都必须是无论如何,不产生危害。开销损失必须尽可能接近忽略不计。事实上,消除测量本身中的极细微的损失是不可能的,但是,在获取性能数据之后,保持其余跟踪进程异步是非常重要的。可以采用若干种模式来实现异步跟踪。图 9 演示了异步跟踪的实现方法概览:

图 9. 异步跟踪

图 9 演示了一个简单的插装截取程序,它通过捕获调用的起始时间来测量它的运行时间,然后将测量数据(运行时间和指标复合名称)分发给处理队列。然后,线程池读取该队列,获取测量数据并完成跟踪流程。

监控设备性能__性能监控系统

源代码中的 Java 类插装

本节将讨论如何实现源代码级插装,并将提供一些最佳实践和示例代码。文章还介绍了一些新的跟踪结构,我将在源代码插装的上下文中阐明它们的操作和它们的插装模式。

虽然其他选择已经流行,但源代码插装在某些实例中是无法避免的;在某些情况下,它是惟一的解决方案。借助一些明智的预防措施,它可以实现良好的效果。需要考虑的事项包括:

在某些情况下,通过其他方法来跟踪具体的项目不太可行。通常,我将这种情况称作上下文跟踪。我使用该术语描述的性能数据并不是很重要,但它为主要数据添加上下文。 上下文跟踪

上下文跟踪受具体的应用程序影响极大,但是可以考虑一个经过简化的例子:含有(long )方法的 - 类。当被调用时,该方法计算并存储各客户员工的薪水。您可以通过各种方法插装该方法,但是,执行中的底层模式清楚表明,调用时间的增加与员工的数量不成比例。因此,研究的运行时间趋势没有上下文可供参考,除非您知道程序每次处理的员工数量。简单来讲,对于特定的时间段,平均耗时x毫秒。无法确定这个值反映的性能是好还是坏,因为您不知道它处理的员工数量是 1 还是 150,而两种情况反映的性能差别巨大。清单 5 在代码中显示了这个简化的概念:

清单 5. 上下文跟踪的例子

public void processPayroll(long clientId) {Collection employees = null;// Acquire the collection of employees//...//...// Process each employeefor(Employee emp: employees) {processEmployee(emp.getEmployeeId(), clientId);}
}

此处的主要挑战是,根据大多数插装技巧,()方法中的任何东西都是不可触及的。因此,虽然能够插装甚至,但是却无法跟踪员工的数量,从而不能为方法的性能数据提供上下文。清单 6 显示了一个拙劣的硬编码示例(且有点效率不高),它将捕获上面提到的上下文数据:

清单 6. 上下文跟踪示例

public void processPayrollContextual(long clientId) {      Collection employees = null;// Acquire the collection of employeesemployees = popEmployees();// Process each employeeint empCount = 0;String rangeName = null;long start = System.currentTimeMillis();for(Employee emp: employees) {processEmployee(emp.getEmployeeId(), clientId);empCount++;}rangeName = tracer.lookupRange("Payroll Processing", empCount);long elapsed = System.currentTimeMillis()-start;tracer.trace(elapsed, "Payroll Processing", rangeName, "Elapsed Time (ms)");tracer.traceIncident("Payroll Processing", rangeName, "Payrolls Processed");log("Processed Client with " + empCount + " employees.");
}

清单 6 中的关键部分是.调用。是指定的收集,它由数值范围限制键控,并且拥有一个表示数值范围名称的值。不再跟踪薪水处理的简单无格式运行时间,清单 6 将员工计数划分为范围,有效分隔运行时间并根据基本类似的员工计数将它们分组。图 10 显示了 APM 系统生成的指标树:

图 10:根据范围分组的薪水处理时间

图 11 演示了根据员工计数划分的薪水处理运行的时间,它揭示了员工数量和运行时间之间的相互关系:

图 11. 各范围的薪水处理运行时间

跟踪程序配置属性允许在属性文件中包括 URL,并能在其中定义范围和阀值(我将简单介绍一下阀值)。属性将在跟踪程序的构造时间被读取,并为.实现提供后台数据。清单 7 显示了 范围的示例配置。我选择使用java.util.的 XML 表示,因为它更能兼容奇怪的字符。

清单 7. 范围配置示例



Payroll Process Range181+ Emps,10:1-10 Emps,50:11-50 Emps,80:51-80 Emps,120:81-120 Emps,180:121-180 Emps

注入外部定义的范围可以使您的应用程序不必频繁更新源代码,这受益于预期的调整和服务水平协议(SLA)在业务方面的变更。当范围和阀值更改生效之后,您只需更新外部文件,而不是应用程序本身。

跟踪阀值和 SLA

外部可配置上下文跟踪的灵活性支持以更加准确和粒度化的方式来定义和测量性能阀值。范围定义一系列数值区间,可以在其中对测量数据进行分类,而阀值是对范围的进一步分类,它根据测量数据的确定范围对获取的测量数据进行分类。在分析收集的性能数据时,一个常见的需求是确定和报告执行是 “成功” 还是 “失败”(因为它们未在指定时间发生)。这些数据的总和可以作为关于系统运行健康状况和性能的通用成绩单,或者作为某种形式的 SLA 遵从性评价。

使用薪水处理系统示例,考虑一个内部服务级目标,它将薪水的执行时间(在定义的员工数范围之内)定义为Ok、Warn和 个区间。生成阀值计数的流程从概念上来说非常简单。您只需为跟踪程序提供您认为是各类别各区间的上限运行时间的值,并引导跟踪程序为分类的运行时间发起一个.,然后 — 为简化报告 — 提供一个总数。表 2 显示了一些经过设计的 SLA 运行时间:

表 2. 薪水处理阀值 员工数 Ok (ms) Warn (ms) (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

使用与范围中相同的 XML(属性)文件中定义的值实现了阀值报告。范围和阀值定义在两个方面稍有不同。首先,阀值定义的关键值是一个正则表达式。当在跟踪一个数值 时,它会检查阀值正则表达式是否匹配被跟踪指标的复合名称。如果匹配,则阀值会将测量数据分类为Ok、Warn或,并为跟踪附加一个额外的.。其次,由于阀值只定义了两个值(根据定义,值大于warn值),因此配置只由两个数值组成。清单 8 显示了之前介绍的薪水处理 SLA 的阀值配置:

清单 8. 薪水处理的阀值配置



1100,1500   280,400   850,1200   900,1100      1400,2000   2000,3000   

图 12 显示添加了阀值指标的薪水处理的指标树:

图 12. 添加了阀值的薪水处理指标

图 13 演示了哪些收集的数据可以表示在饼形图中:

图 13. 薪水处理的 SLA 汇总(1 到 10 名员工)

确保查找上下文和阀值分类的效率和速度非常重要,因为它们在完成实际工作的线程中执行。在实现中,所有指标名称在第一次被跟踪程序发现时,将存储在(线程安全)为具备和不具备阀值的指标指定的映射中。当特定指标的跟踪事件发生后,阀值确定过程占用的时间是一个Map查找时间,它的速度通常足够快。如果阀值条目或指标名称的数量非常大,则一种合理的解决方案是推迟阀值确定,并在异步跟踪线程池中处理它们。

第 1 部分结束语

本系列文章的第 1 部分介绍了一些监控反面模式和一些 APM 系统需要的属性。我总结了一些通用性能数据收集模式,并介绍了接口,我将在本系列文章的其余部分继续使用它。我已经演示了监控 JVM 健康状况的技巧,以及如何通过 JMX 获取通用性能数据。最后,我总结了各种实现高效和防代码更改的源代码级插装方法(用于监控原始性能统计数据和上下文派生统计数据),以及如何使用这些统计数据生成关于应用程序 SLA 的报告。第 2 部分将探究插装 Java 系统而无需修改应用程序源代码的技巧,具体方法是使用截取、类包装和动态字节码插装。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了