为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

Java 运行时监控

2012-02-20 50页 doc 2MB 65阅读

用户头像

is_584850

暂无简介

举报
Java 运行时监控Java 运行时监控 目录: 第一部分 第二部分 第三部分 第 1 部分: Java 系统运行时性能和可用性监控 技巧和模式 Nicholas Whitehead, 高级技术架构师, ADP Nicholas Whitehead 在 ADP 的 Small Business Services 部门担任高级技术架构师,该公司坐落在美国新泽西州的弗朗汉姆公园。他拥有 10 余年 Java 开发的经验,所涉及的行业包括银行投资、电子商务和商业软件。在开发和支持生产应用程序(其中一些是他自己的)方面的经验一直鼓舞着他学习和实现性能管...
Java 运行时监控
Java 运行时监控 目录: 第一部分 第二部分 第三部分 第 1 部分: Java 系统运行时性能和可用性监控 技巧和模式 Nicholas Whitehead, 高级技术架构师, ADP Nicholas Whitehead 在 ADP 的 Small Business Services 部门担任高级技术架构师,该公司坐落在美国新泽西州的弗朗汉姆公园。他拥有 10 余年 Java 开发的经验,所涉及的行业包括银行投资、电子商务和商业软件。在开发和支持生产应用程序(其中一些是他自己的)方面的经验一直鼓舞着他学习和实现性能管理系统。 简介: 运行时性能监控对于实现和维护性能优异的系统至关重要。本文是系列文章的第 1 部分(共 3 部分),Nicholas Whitehead 将阐述如何有效地对 Java™ 性能执行低级粒度的监控。您生成的数据可以很好的反映系统操作的内在状态,并揭示影响环境稳定性及性能的约束和因素。 查看本系列更多内容 本文的标签:  java 标记本文! 发布日期: 2008 年 8 月 19 日 级别: 中级 其他语言版本: 英文 访问情况 : 4147 次浏览 评论: 0 (查看 | 添加评论 - 登录) 平均分 (3个评分) 为本文评分 简介 当今的许多 Java 应用程序都依赖于一组复杂的分布式依赖关系和移动部件。很多外部因素都可能对应用程序的性能和可用性造成影响。这些影响基本上都无法完全消除或解决,且难以在预生成环境中准确模拟。Stuff happens。但是,您可以创建并维护一个全面的系统来监控应用程序的整个生态系统,从而显著降低这些事件的严重性和持续时间。 本系列文章给出了实现此类系统的一些模式和技巧。模式,以及我将使用的一些术语,都表示泛指。通过结合示例代码和插图,它们将帮助您理解应用程序性能监控的概念。这种理解强调解决的必要性,并能帮助您选择商业或开源的解决方案。您可以扩展和定制一个解决方案,或者根据需要将其作为设计解决方案的蓝图。 第 1 部分: · 探究应用程序性能管理(APM)系统的属性 · 描述系统监控的常见反面模式 · 列举监控 JVM 性能的方法 · 提供有效插装应用程序源代码的方法 第 2 部分将重点介绍插装 Java 类及资源而无需修改原始源代码的方法。第 3 部分将论述监控 JVM 外部资源的方法,包括主机及其操作系统以及数据库和消息传递系统等远程服务。它还将总结并归纳其他的 APM 问题,如数据管理、数据虚拟化、报告和报警。 回页首 APM 系统:模式和反面模式 为让大家正确入门,应当强调,虽然此处介绍的多数与 Java 相关的内容看上去与应用程序和代码性能分析的类似,但其实并非 如此。性能分析是一个极具价值的生产前流程,它可以确认您的 Java 代码是否可扩展、高效、快速和足够出色。但是,根据 stuff happens 公理,当您在生产中遇到无法说明的问题时,优秀的开发阶段代码性能分析可能无用武之地。 我的意思是,在生产中实现性能分析的一些方面,并从运行中的应用程序收集一些相同的实时数据及其所有外部依赖关系。该数据由一系列遍及目标的定量测量指标组成,它们为整个系统的健康状况提供细粒度和详细的表示。此外,通过保留这些指标的历史库,您可以捕获准确的基线,以帮助您确认环境仍然健康,或查明特定缺陷的根源和规模。 监控反面模式 完全没有监控资源的应用程序微乎其微,但仍然需要考虑这些反面模式,它们经常出现在运行环境中: 孤立监控的缺陷 当系统视图无法反映整体情况时,便会出现孤立 监控。最为复杂和难以诊断的问题通常涉及多个参与和相关组件。请考虑一个简单的例子,托管在 Java 应用服务器上的应用程序(所有者未知)实现了一个有故障的 JDBC 连接池类(泄漏连接)。 当应用程序所有者查看管理接口时,他们声称自己的服务器与数据库保持了 100 个连接。相反,数据库管理员(DBA)查看数据库管理控制台却能看到相同的主机实际维持了 120 个连接,并且其数量正在迅速增加。在经过整合的 APM 系统中,创建一个显示这两种指标的曲线图应该说是微不足道的。当这两个数字彼此背离时,看到该图的任何人都可以立即清楚地看到真实数字,并能准确判断出问题所在。 · 盲点:某些系统依赖关系未受监控,或者监控数据不可访问。运行中的数据库可以覆盖所有监控范围,但如果受支持的网络无法全面覆盖,则诊断小组在分析数据库性能和应用服务器症状时将无法看到网络中的故障。 · 黑盒:核心应用程序或者它的某个依赖关系对于其内部可能不具有监控透明性。JVM 是一个不折不扣的黑盒。举例来说,诊断小组正在调查 JVM 中的莫名延时问题,并且只拥有支持操作系统的统计数据(如 CPU 利用率和进程需要的内存大小),则他们可能无法诊断垃圾收集或线程同步问题。 · 脱节和断开的监控系统:应用程序可以由大型共享数据中心托管,其中,依赖关系由一系列共享资源组成,比如说数据库、存储区网络(SAN)库、消息传递及中间件服务。组织有时高度孤立,各小组只负责管理自己的监控和 APM 系统(请参阅 孤立监控的缺陷 侧栏)。没有各依赖关系的整合视图,各组件所有者只能管中窥豹,只见一斑。 图 1 对比了孤立和整合的 APM 系统: 图 1. 孤立和整合 APM 系统的对比 · 事后报告和相关性:为尝试解决孤立监控的问题,运营支持小组可以运行定期进程获取各来源的数据,将这些数据整合到一个地方,然后再生成汇总报表。这种方法有时效率低下且不切实际,因为它需要按照指定频率严格执行,而缺乏实时数据也会对诊断小组当场发现问题的能力产生负面影响。此外,事后聚合有时缺乏足够的粒度,从而导致重要模式隐藏在数据中不被发觉。举例来说,某个报告可能显示某特定服务调用昨天平均耗时 200 毫秒,但却隐藏了它在下午 1:00 到 1:45 间平均耗时 3500 毫秒。 · 定期或随需应变的监控:由于某些工具强制占用较高的资源开销,因此不能(或不应)经常使用它们。结果,它们很少收集数据,或者只在检测到问题后才收集数据。因此,APM 系统只能执行最低基线,而无法在问题恶化前提前报警,并且可能会自己加剧势态的严重性。 · 非持久化监控:许多工具都提供了有用的性能和可用性指标实时显示功能,但它们并不支持持久化指标供长期或短期比较和分析的功能。常见的一种情况是,如果缺少历史上下文,则性能指标将毫无价值,因为没有判断指标优劣的基准。举例来说,当前的 CPU 利用率是 45%。如果不知道历史利用率的情况,则不好判断当前 CPU 利用率负荷的轻重程度。但是,如果知道历史的典型值为百分之 x,可接受的用户性能上限是百分之 y,则情况就大有改观了。 · 对生产前模型的依赖:假设所有潜在问题都可在生产部署之前从环境中清除,则完全依赖生产前监控和系统模型的实践经常会导致运行时监控不够全面。这些假设无法解决不可预测事件和依赖性故障,因此,诊断小组在遇到此类事件时将没有工具和数据可用。 整合 APM 的实现并不排除监控和诊断工具,如 DBA 管理工具集、低级网络分析应用程序和数据中心管理解决方案。这些工具仍然是无价的资源,但如果它们依赖于整合视图的专有性,则难以克服孤立效果的影响。 理想 APM 系统的属性 与刚才讨论的反面模式相反,本系列文章介绍的理想 APM 系统拥有以下属性: · 渗透力:它监控所有应用程序组件和依赖关系。 · 粒度化:它可以监控层次极低的函数。 · 整合性:收集的所有指标将被发送到支持整合视图的同一逻辑 APM 中。 · 恒定:一周 7 天,一天 24 小时不间断监控。 · 高效:性能数据收集不会对监控目标造成不利影响。 · 实时:可以实时显示、报告和警告监控的资源指标。 · 历史:监控的资源指标将持久化存储在一个数据库中,因此可以查看、比较和报告历史数据。 在深入研究此系统的实现细节之前,了解 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 系统 图 2 还展示了 APM 系统提供的常用服务: · 实时显示:近乎实时显示选定指标的图表。 · 报告:生成的指标活动报告。这通常包括一系列固定报告和自定义报告,并能导出数据供用户在别处使用。 · 历史库:包含原始或汇总指标的历史数据库,从而能够查看特定时间范围内的图表和报告。 · 报警:将收集指标确定的具体条件通知相关个体或组的功能。典型的报警方法是电子邮件和某种类型的自定义钩子接口,可以允许运营小组将事件传播给事件处理系统。 公共跟踪 API 在 APM 的目标环境中的实现和应用提供了一些一致性。此外,自定义收集器的目的是让开发人员能够专心获取性能数据,而不必担心跟踪的问题。下一节将介绍解决此问题的 APM 跟踪接口。 回页首 ITracer:跟踪程序接口 Java 语言可以很好地充当收集器的实现语言,因为: · 广泛的平台支持。Java 收集器类可以在大多数目标平台上运行,而无需修改。这使监控架构可以在本地灵活地使用 PDS 合并收集器进程,而不需要使用远程收集。 · 出色的性能(但是会随可用资源而变化)。 · 健壮的并发和同步执行支持。 · 支持一组丰富的通信协议。 · 受第三方 API 的广泛支持,比如 JDBC 实现、SNMP 和专用 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 类图: 图 3. ITracer 接口和工厂类 跟踪类别和名称 ITracer 的基本前提是向中央 APM 系统提交一个度量和相关的名称。此活动由 trace 方法实现,该方法因提交的度量而有所不同。各 trace 方法都接受一个 String[] name 参数,其中包含复合名称的上下文组件,其结构特定于 APM 系统。复合名称向 APM 系统指示提交的名称空间和实际的指标名称;因此,复合名称中通常至少包括根类别和度量说明。底层 ITracer 实现应该知道如何通过传递的 String[] 构建复合名称。表 1 演示了复合命名约定的两个示例: 表 1. 示例复合名称 名称结构 复合名称 简单斜杠分隔 Hosts/SalesDatabaseServer/CPU Utilization/CPU3 JMX MBean ObjectName com.myco.datacenter.apm:type=Hosts,service=SalesDatabaseServer,group=CPU Utilization,instance=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 %"); ); 跟踪程序度量数据类型 在此接口中,度量数据可以是以下类型: · int · long · java.util.Date · String APM 系统提供商可能支持其他数据类型的收集度量数据。 跟踪程序类型 选定了具体的度量数据类型(如 long)之后,可以根据 APM 系统支持的类型来选择解释特定值的方式。还需记住,各 APM 实现可以使用不同的术语来表示本质相同的类型,并且 ITracer 使用了一些通用的命名规则。 时间间隔 讨论跟踪程序类型需要了解时间间隔 的概念。请考虑这样一个概念:进程正在收集操作已经运行的时间并将它发送给 APM 系统。您每分钟都可以看到成百上千的调用。传输并存储每个度量的细节并不是切实有效的方法,而且也不应该在任何性能报告或图表中反应每一个度量,因为即便非常罕见的调用也可以歪曲整个信息表示。同时,由于长时期过度捕获,您将丧失粒度化的概念,因为这段时间内的合法峰值可能相当多。 解决此问题的一种模式是选取一个时间时隔来表示您希望聚合的最低粒度。1 个小时可能太长,而 200 毫秒又太短,因此您可以设定 30 秒的间隔。现在,您仍然可以对假设操作的每次调用都调用 trace,但是,保留在各时间间隔末尾的数据是: · 时间间隔平均耗时 · 时间间隔最长耗时 · 时间间隔最短耗时 · 时间间隔内的调用次数 · 时间时隔开始时刻的时间戳 这是过度聚合度量和存储所有单独度量的一种有效的折衷方法。 ITracer 中表示的跟踪程序类型: · 平均时间间隔:trace(long value, String[] name) 和 trace(int value, String[] name) 方法将发出时间间隔平均值的跟踪(请参阅 时间间隔 侧栏)。这表示每个提交将被转化为当前时间间隔的聚合值。当新时间间隔开始时,聚合值计数器将重置为零。 · 粘附: traceSticky(value long, String[] name) 和 traceSticky(value int, String[] name) 方法发出粘附值跟踪。这表示,与时间间隔平均指标相反,聚合将它们的值保留在时间间隔中。如果现在跟踪值 5,而此后不再执行跟踪直到第二天某个时刻,则该指标将保持为 5,直到提供了新值。 · 增量:增量跟踪将传递一个数值,但提供给 APM 系统(或由 APM 系统解释)的实际值是此度量与前一度量之间的增量。它们有时被称作 rate 类型,用于反映自己的性能优势。请考虑事务管理程序的提交总数度量值。该数字始终在增加,并且其绝对值几乎没有用处。该数字有用的地方是它增加的速率,因此定期收集它的绝对值并跟踪每次读取数据之间的增量可以反映事务提交的速率。增量跟踪比平均时间间隔和粘附方式的跟踪更为常用,但仍有些用例采用了平均时间间隔。增量跟踪必须能够区分只能增加的度量和同时能增减的度量。小于前值的提交度量应被忽略或造成底层增量重置。 · 事件:这种类型是一种简单的非聚合指标,它表示特定事件在时间间隔内发生的次数的增量计算。由于收集器和跟踪程序都不期望知道特定时刻的运行总数,因此基本的 traceIncident(String[] name) 调用没有指定任何值,并且隐式只增加一次事件增量。当需要计算多次增量时,除了在循环中多次调用该方法之外,另一种较好的方法是通过 traceIncident(int value, String[] name) 方法根据 value 来计算合值。 · 智能:智能跟踪程序是一个参数化的类型,它与跟踪程序中的某种其他类型相映射。度量值和跟踪类型将作为 String 传递,并且可将可用类型作为常量定义在接口中。当收集器不知道正在收集的数据的类型或跟踪程序类型时,这是一个非常方便的方法,但是也可以直接将收集值和配置的类型名称传递给跟踪程序。 TracerFactory 是一个普通的工厂类,用于根据传递的配置属性创建新 ITracer 实例,或者从缓存中引用已创建的 ITracer。 收集器模式 收集通常有三种可选模式,这影响到应该使用的跟踪程序类型: · 轮询:按固定频繁调用收集器,它将检索和跟踪 PDS 中的指标或指标集的当前值。例如,可以每分钟调用一次收集器来读取主机的 CPU 利用率,或通过 JXM 接口从事务管理器读取提交事务的总数。轮询模式的前提是对目标指标的定期采样。因此,对于轮询事件,指标的值将提供给 APM 系统,但是,假定中间时期的值不变。因而,轮询收集器通常使用粘附跟踪程序类型:APM 系统在生成报告时将假定所有轮询事件之间的值不变。图 4 演示了此模式: 图 4. 轮询收集模式 · 监听:这种通用数据模式是 Observer 模式的一种形式。收集器将其自身注册为目标 PDS 的事件监听程序,它将在相关的事件发生时接受回调。作为回调结果发出的跟踪值取决于回调有效负荷本身的内容,但收集器至少可以跟踪每个回调的事件。图 5 演示了此模式: 图 5:监听收集模式 · 截取:在此模式中,收集器将自己作为截取程序插入到目标和它的调用程序之间。对于通过该截取程序的各个活动实例,截取程序将生成一个度量并跟踪它。当截取模式是 request/response 时,收集器可以测定请求数量、响应时间、请求或响应的有效负荷。例如,HTTP 代码服务器可以充当收集器,它可以: · 计算请求数,可以选择根据 HTTP 类型(GET 和 POST 等)或统一资源标识符(URI)来分类。 · 请求的响应时间。 · 测定请求和响应的大小。 由于您可以假定截取收集器能 “看到” 每一个事件,因此实现的跟踪程序通常为平均时间间隔类型。因此,如果时间间隔到期且没有活动发生,则该时间间隔的聚合值将为零,而与之前时间间隔中的活动无关。图 6 演示了此模式: 图 6. 截取收集模式 现在,我已经介绍了性能数据跟踪 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 数据。这些问题大多都有相应的解决方法,但是它们的多变性使本文无法详细介绍这方面的内容。 在本地部署中,收集器和它的调用调度程序部署在目标 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 是摘录自 JMXCollector collect() 方法的代码段,它显示了 ThreadMXBean 中的收集和线程跟踪活动。点击 此处 查看完整清单: 清单 2. 示例 JMX 收集器的 collect() 方法的部分代码,它使用 ThreadMXBean . . 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 Monitoring on = 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"); . . // Done long 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 中的代码将跟踪 TotalThreadsStarted 和 CurrentThreadCount 的值。由于它是轮询收集器,因此两个跟踪都使用粘附选项。但是,由于 TotalThreadsStarted 是一个不断增加的数值,因此最吸引人的地方不是绝对值,而是已创建线程的速率。这样,该跟踪程序将使用 DeltaSticky 选项。 图 7 显示了此收集器创建的 APM 指标树: 图 7. JMX 收集器 APM 指标树 JMX 收集器的一些方面并未显示在清单 2 中(但是可以在 完整源代码 中看到),比如说调度注册,它将每隔 10 分钟为 collect() 方法创建一个定期回调。 在清单 2 中,不同跟踪程序类型和数据类型的实现方法将由数据源决定。例如: · TotalLoadedClasses 和 UnloadedClassCount 将作为粘附增量被跟踪,因为它们的值始终递增,而且增量在测定类加载活动方面比绝对值更加有用。 · ThreadCount 变量可增加或减少,因此它将作为粘附类型被跟踪。 · 收集错误 将作为内部事件被跟踪,它将在收集遇到异常时递增。 为了追求效率,由于目标 MXBeans 的 JMX ObjectName 在目标 JVM 的生存期不会更改,因此收集器使用 ManagementFactory 常量名来缓存名称。 对于 MXBeans 的两种类型 — GarbageCollector 和 MemoryPool — 准确的 ObjectName 无法预先知晓,但是您可以提供一个通用的模式。在这些情况下,在初次执行收集时,您将对 MBeanServerConnection 发起一个查询,并请求与提供模式相匹配的所有 MBeans 的列表。为避免未来在目标 JVM 的生存期执行查询,返回的匹配 MBean ObjectName 将缓存在内存中。 在某些情况下,收集的目标 MBean 属性可能不是纯数值类型。MemoryMXBean 和 MemoryPoolMXBean 就是这种情况。对于这些情况,属性类型是可查询键和值的 CompositeData 对象。对于 java.lang.management JVM 管理接口,MXBean 标准采用了 JMX Open 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 收集器经过了适当简化,但它仅限于硬编码的收集方式。理想情况下,收集器将实现数据访问方式,而外部提供的配置将提供内容。这种设计使收集器更具实用性,且易于重用。对于最高级别的重用,外部配置的收集器应该支持这些配置点: · PDS 连接工厂指令,为收集器提供用于连接到 PDS 的接口,以及在连接时使用的配置。 · 执行收集的频率。 · 尝试重新连接的频率。 · 收集的目标 MBean,或通配符形式的对象名称。 · 对于各目标,跟踪复合名称或者应该跟踪的度量片段,以及应该跟踪的数据类型。 清单 3 演示了 JMX 收集器的外部配置: 清单 3. JMX 收集器的外部配置示例 collectors.jmx.RemoteRMIMBeanServerConnectionFactory jmx.rmi.url=service:jmx:rmi://127.0.0.1/jndi/rmi://127.0.0.1:1090/jmxconnector AppServer3.myco.org,JMX 10000 注意,TargetAttribute 元素包含一个名为 type 的属性,它表示智能类型跟踪程序的参数化变量。SINT 类型表示粘附 int,SDINT 类型表示增量粘附 int。 回页首 通过 JMX 监控应用程序资源 目前为止,我已经讨论了通过 JMX 监控惟一标准的 JVM 资源。但是,许多应用程序架构,如 Java EE,可以通过 JMX 公开重要的特定于应用程序的指标(这取决于供应商)。一个典型的例子是 DataSource 利用率。DataSource 是一个用于将连接池化到外部资源(通常为数据库)的服务,这限制了并发连接的数量,以保护资源不受恶意应用程序的占用。监控数据源是整个监控中的关键环节。由于 JMX 抽象层,该流程与之前介绍的类似。 下面是来自 JBoss 4.2 应用服务器实例的典型数据源指标: · 可用连接数:当前池中可用连接的数量。 · 连接数:连接池与数据库建立的实际物理连接的数量。 · 最大使用连接数:池中正在使用的连接的上限标记。 · 正在使用的连接数:当前正在使用的连接数量。 · 已创建的连接数:为该池创建的连接总数。 · 已部署的连接数:为该池部署的连接总数。 现在,收集器将使用批属性检索,并在一个调用中获取所有属性。惟一需要注意的是,您需要查询返回的数据,以接通不同的数据和跟踪程序类型。DataSource 指标在没有活动时也是不会变化的,因此,要使数值变化,您需要生成一些负载。清单 4 显示 DataSource 收集器的 collect() 方法: 清单 4. DataSource 收集器 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()); } } } // Done long 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 显示了 DataSource 收集器的相应指标树: 图 8. DataSource 收集器指标树 回页首 监控 JVM 中的组件 交叉注册 MBeans 在许多情况下,可以将目标 MBeans 注册到相同 JVM 的不同 MBeanServer 中。例如,java.lang MXBean 注册在平台代理(也称作 JVM MBeanServer)中,而 JBoss 服务器中的 DataSource MBeans 位于 jboss MBeanServer 中。在远程监控实现中,这种情况有时会增加额外的开销和配置复杂度,因为每个 MBeanServer 都需要两个远程连接。此外,为远程连接公开平台 MBeanServer 也会带来额外的开销。在这些情况下,通常采用交叉注册 MBeans 的方式,这样所有的目标 MBeans 都可以通过相同的 MBeanServer 接口来监控。请查阅本文的 源代码,获取名称为 map-platform-mxbeans.bsh 的示例 bean-shell 脚本。如果您将此脚本部署到 JBoss 服务器中,它将在 JBoss MBeanServer 中交叉注册平台 MBeanServer 的 MXBeans。 本节介绍的技巧可用于监控应用程序组件、服务、类和方法。相关的主要区域如下: · 调用速率:调用服务或方法的速率。 · 调用响应速率:服务或方法响应的速率。 · 调用错误率:服务或方法生成错误的比率。 · 调用运行时间:调用在每个间隔时间内的平均、最短和最长运行时间。 · 调用并发性:并发调用服务或方法时执行的线程数。 使用 Java SE 5(和更新版本)ThreadMXBean 的一些实现提供的指标,还可以收集以下指标: · 系统和用户 CPU 时间:调用某方法占用的 CPU 时间。 · 等待数量和总等待时间:调用某方法或服务时,等待线程的实例数量和总占用时间。当线程进入 WAITING 或 TIMED_WAITING 等待状态并暂停另一个线程的活动时将发生等待事件。 · 阻塞数量和总阻塞时间:在调用某个方法或服务时,处于 BLOCKED 状态的线程的实例数量和总占用时间。当线程等待监控锁进入或重新进入同步阻塞时会发生阻塞事件。 还可以使用备选工具集和本机接口来确定这些指标和其他指标,但这通常涉及某种级别的开销,从而造成不必要的生产运行时监控。已经说过,指标本身,甚至在收集时,是低级的。它们的作用也许仅限于分析趋势,并且很难与无法通过其他手段确定的因果效应相关联。 所有上述指标都可以通过插装类和方法的流程来收集,以便于收集和跟踪目标 APM 系统的性能数据。可以采用各种技巧来直接插装 Java 类,或者通过它们来间接计算性能指标: · 源代码插装:最基本的技巧是在源代码阶段添加插装,这样编译和部署后的类就已经在运行时包含了插装。在某些情况下,这种方法具有意义,并且一些特定的实践使它成为可行的流程和投资。 · 截取:通过截取程序(执行测定和跟踪)转移调用,可以实现有效和准确的跟踪,而无需接触目标类、它们的源代码和运行时字节码。这种实践简单可取,因为存在许多 Java EE 框架和其他流行的 Java 框架: · 通过配置支持抽象。 · 支持类注入和通过接口引用。 · 有某些情况下直接支持截取栈概念。执行流程经过定义了配置的对象栈,其作用是接收调用并执行一些处理,然后继续传递。 · 字节码插装:该流程将字节码注入到应用程序类中。注入的字节码将添加性能数据收集插装,该插装被作为新类的一部分调用。这个流程有时极为有效,因为插装是完全经过编译的字节码,并且代码的执行路径以最细化的方式扩展,同时仍然能够收集数据。它的另一个优点是无需修改初始源代码,并且其对环境的配置更改也可能最少。此外,通用模式和字节码注入技巧允许对源代码不可用的类和库进行插装,许多第三方类属于这种情况。 · 类包装:该流程使用另一个类来包装或替换目标类,前者实现了相同功能,同时也包含了插装。 在本文的第 1 部分中,我只讨论基于源代码的插装;您将在第 2 部分中了解更多关于截取、字节码插装和类包装的信息。(从拓扑学的角度来说,截取、字节码插装和类包装的本质完全相同,但它们实现结果的操作有稍微不同的含义)。 异
/
本文档为【Java 运行时监控】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索