如何诊断 J2EE 系统中的性能问题

如何诊断 J2EE 系统中的性能问题

时间:2004-12-14
作者:John Bley
浏览次数: 
本文关键字:诊断, J2EE, 性能, 测试, 瓶颈
文章工具
 推荐给朋友
 打印文章
现在要求您诊断  WebLogic J2EE 应用程序中的性能问题。因为 Java 系统是如此复杂,所以诊断  WebLogic J2EE 应用程序中的性能问题就有点像诊断疑难杂症。
为了正确地找到问题所在,您需要对症状有全面了解,要做好准备进行大量研究工作,最后您需要制定正确的治疗方案。本文讨论了 J2EE 应用程序性能问题的一些最常见类型和它们产生的原因,以及如何正确地诊断和消除它们的推荐指导原则。


症状
   WebLogic 应用程序性能问题的症状是什么?您看到的症状可以指导您在所有可能的问题中进行搜索。请准备一个笔记本并开始向人们调查。试着把对问题根本原因的推测和假设与系统行为的实际证据分离。下面是一个常见症状集的列表:
  • 持续缓慢:应用程序一直特别慢。改变环境因素(负载、数据库连接数量)对整体响应时间的改变很小。
  • 随着时间推进越来越慢:系统运行时间越长(负载相对均衡不变的情况下),就变得越慢。有可能是(最后)达到了某个阈值,系统被锁定或者由于大量错误而崩溃。
  • 随着负载增加越来越慢:每增加一个额外用户,应用程序就变得越慢。如果用户离开系统,系统就“冷却下来”,恢复正常。
  • 零星的挂起或者异常错误:偶尔(可能由于负载或某些其它原因),在页面无法完成或者出现追踪到异常和堆栈的错误页时,用户会看到挂起的情况。挂起的数量可能不同,但总是无法完全消除,甚至在强化 (“burn in”) 期间之后也是如此。
  • 可以预见的锁定:首先出现一些挂起或错误,然后加速出现,直到系统完全被锁定。通常这些问题可以通过“重新启动来管理”的方式解决。
  • 突然混乱:系统一直运行正常,相当一段时间里(可能一个小时,也可能是三天)性能都还差强人意,但是“突然某个时候,根本没有任何原因的”,系统开始出现大量错误,或者被锁定。

为什么问题诊断如此复杂?
  对于 WebLogic 应用程序的某个具体应用模式来说,没有既定的公式可以用来推导出它的性能(在严格的实时工程当中,速度单调分析这类技术确实可以做这项工作,但是在本文里 还是让我们忘记它吧)。网络上是否存在另外一个系统正在密集使用一个共享的后端服务,对于实际产生的性能有很大影响。性能也许还取决于 JDBC 驱动程序版本和数据库的正确匹配。也许开发人员三年前写的一些代码恰巧在这个时候才出现特定类型的异常,而您急切需要的解决问题回馈,却恰恰包含在这个异 常里。 
  从本质上说,典型业务系统的性能是由成千上万交互的变量和决策共同作用的结果。就像人体一样,有太多连锁着的部分和过程,所以很难理解系统的整体。因此我们进行了简化,并求助于拱形模式。 

疾病
  您看到的症状的根本原因是什么?它是初级流行性感冒或者是肺炎的开始吗?是应用程序内部的底层问题还是它所在的 JVM 外部的问题?请参阅表 1 中应用程序性能低下的一些最常见原因。

表1

毛病 描述 症状 原因或治法
线性内存泄漏 每单位(每事务、每用户等)泄漏造成内存随着时间或负载线性增长。这会随着时间或负载增长降低系统性能。只有重启才有可能恢复。 随着时间越来越慢
随着负载越来越慢
虽然可能有多种外部原因,但最典型的是与资源泄漏有关(例如,每单位数据的链表存储,或者没有回收的回收/增长缓冲区)。
指数方式内存泄漏 双倍增长策略的泄漏造成系统内存消耗表现为时间的指数曲线 随着时间越来越慢
随着负载越来越慢
通常是由于向集合(Vector,HashMap) 中加入永远不删除的元素造成的。
糟糕的编码:无限循环 线程在 while(true) 语句以及类似的语句里阻塞。 可以预见的锁定 您需要对循环进行大刀阔斧的删剪。
资源泄漏 JDBC 语句,CICS 事务网关连接,以及类似的东西被泄漏了,造成对 Java 桥接层和后端系统的影响。 随着时间越来越慢
可以预见的锁定
突然混乱
通常情况下,这是由于遗漏了 finally 块,或者更简单点,就是忘记用 close() 关闭代表外部资源的对象所造成的。
外部瓶颈问题 后端或者其他外部系统(如鉴权)越来越慢,同样减缓了 J2EE 应用服务器和应用程序 持续缓慢
随着负载越来越慢
咨询专家(负责的第三方或者系统管理员),获取解决外部瓶颈问题的方法。
外部系统 J2EE 应用程序通过太大或太多的请求滥用后端系统。 持续缓慢
随着负载越来越慢
清除冗余的工作请求 ,成批处理相似的工作请求,把大的请求分解成若干个更小的请求,调整工作请求或后端系统(例如,公共查询关键字的索引)等。
糟糕的编码:CPU密集的组件 这是 J2EE 世界中常见的感冒。一些糟糕的代码或大量代码之间一次糟糕的交互,就挂起了 CPU,把吞吐速度减慢到爬行的速度。 持续缓慢
随着负载越来越慢
典型的解决方案就是数据高速缓存或者性能计数。
中间层问题 实现得很糟糕的桥接层(JDBC 驱动程序,到传统系统的 CORBA 连接),由于对数据和请求不断的排列、解除排列,从而把所有通过它的流量减慢到爬行速度。这个毛病在早期阶段很容易与外部瓶颈混淆。 持续缓慢
随着负载越来越慢
检查桥接层和外部系统的版本兼容性。如果有可能,评估不同的桥接供应商。如果重新规划架构,有可能完全不需要桥接。
内部资源瓶颈:过度使用或分配不足 内部资源(线程、放入池的对象)变得稀缺。是在正确使用的情况下加大负载时出现过度使用还是因为泄漏? 随着负载越来越慢
零星的挂起或异常错误
分配不足:根据预期的最大负载提高池的最大尺寸。过度使用:请参阅外部系统的过度使用。
不停止的重试 这包括对失败请求连续的(或者在极端情况下无休止的)重试。 可以预见的锁定
突然混乱
可能就是后端系统完全宕机。在这里,可用性监控会有帮助,或者就是把尝试与成功分开。
线程:阻塞点 线程在过于积极的同步点上备份,造成交通阻塞。 随着负载越来越慢
零星的挂起或异常错误
可以预见的锁定
突然混乱
可能同步是不必要的(只要重新设计),或者比较外在的锁定策略(例如,读/写锁)也许会有帮助。
线程:死锁/活动锁 最普遍,这是您基本的“获得顺序”的问题。 突然混乱 处理选项包括:主锁,确定的获得顺序,以及银行家算法。

测量关键的统计指标
  当您负责诊断问题的时候,您应当能够跟踪关于您的 WebLogic 应用程序健康情况的关键统计指标。您能测量什么?有什么工具可以提供帮助呢?

  • 使用的全部内存:在不同级别上 (JVM 堆栈,操作系统),Java 堆栈 profiler 对堆栈的正确使用提供了可见性;像 top ,vmstat 以及 Windows Perfmon 这样的工具在操作系统级别上为内存使用提供可见性。察看 Java 堆栈的一个简单的聚合视图,请打开―verbose:gc 开关(如果在您的 JVM 上可以使用的话)。
  • CPU 时间:合并(可以通过使用 top 等方式得到)每个组件或每种方法。其中某些指标可以通过 WebLogic 管理控制台得到。也可以通过 Java profile 使用它们。
  • 计时 (a.k.a.“实” 时):每事务,每组件,每方法;可以按统计平均值或单独的数据点查看。Java profiler 可以产生一些这样的数据,但是使用程序监控解决方案可能是您的最佳选择。
  • 内部资源:分配的数量,使用的数量,等待的客户数量,获得资源的平均等待时间,使用资源平均消耗的时间,使用资源完成请求工作时使用的平均时间。应用程序服务器通常会给出这些数字的最小可视性。
  • 外部资源:分配的数量,使用的数量,等待的客户数量,平均等待时间,加上对这些外部系统的直接测量(例如查看它能多快完成请求的工作)。不要忘记运行应用程序服务器的操作系统和硬件也是“外部资源”-例如,是否您使用了太多的进程或端口?可以用两种形式测试这些资源―从 JVM 内部测试提供资源的桥接层,用外部资源本身的工具测量外部资源。
  • 网络应用:带宽使用,延迟。虽然操作系统自带的工具(例如 netstat)也有助于做这些工作,但是网络嗅探器设备可以深入了解这些信息。
  • 系统状态:用线程清除,日志和跟踪文件,堆栈跟踪等等。或者在更深层次上,就像调试器中查看的那样使用变量的值。

实验工作
  有的时候,在一次标杆运行中获得的数据,不足以揭示答案。那么找到答案的机会就在于您还有些有限的预算,可以运行试验或者做实验工作,来完成诊断。您可以运行什么类型的试验呢?您可以修改、观察什么变量呢?

  • 尝试观察系统行为在一段时间上的变化。应用一个衡定的负载,并观察您的指标随时间发生的变化。您可能会看到某些趋势,短则一小时就可看到,长则二三天。例如,您可以看到内存使用随着时间而增长。随着使用的内存数量达到上限, JVM 花在搜索垃圾上的时间和操作系统花在分配内存页面上的时间开始减少用户事务的整体响应时间。当抛开 GC 的时候,垃圾搜集的整个过程可能过长,从而造成执行中的事务超时和异常。现在可以开始查找资源泄漏或内存泄漏了。
  • 尝 试在系统上改变负载。 采用三到四种工作负载(例如,10个,50个和100个用户),并搜集每个负载的数据。分析一下您对这些负载的测量情况。例如,您可能发现,当您的账户登 录 servlet 的响应时间无论如何都会低于 50 毫秒的时候,计算销售税的 EJB 却随着用户数据的增长,速度呈线性下降。如果这个 EJB 性能在负载下的差异能解释整体响应时间性能在负载下的增长,那么现在就是深入分析这个组件的时候了。谨记一定还要把负载恢复原状,并查看系统是否复原。
  • 尝试把系统分成小单元,针对每个单元轮流进行压力测试。 选择一个或多个坐标系对系统进行划分。主要的一个是系统面上的层:负载均衡器、Web 服务器、WebLogic Server,以及后端。其它示例包括用户账户,内部组件,事务类型,以及单独的页面。假设您选择了用户账户。在用户 A 的账户下运行一些负载,然后再在用户 B 的账户(应当非常不同)下运行某些负载;比较两次运行间不同的测量结果。或者,轮流选择您使用的后端系统,分别对使用每个后端系统比较重的应用程序组件进行压力测试。具体要选择哪个坐标进行划分,完全取决于您要证明或者否定哪个假设。下面是一些具体想法:
      - 如果某个用户的登录看起来造成了问题,那么有可能是这个用户账户档案(例如,装入 2,000 个订单的完整采购历史),或者可能是他使用系统的方式(例如,页面访问的顺序,或者他用来查找某个文档的查询字符串的正确性)。
      - 如果您使用的是一个集群系统,请尝试以单台机器为单位划分。尽管尽了最大努力,有的时候还会有一些机器没有安装最新的应用服务器或者操作系统补丁,这就会 造成不同的性能特征。而且,还请注意负载均衡器,或者保姆进程,查看它们是否公平地分配了工作并跟踪了进入请求。

诊断:测试您的推测
  在这个时候,您应当已经有了足够的信息,可以形成关于性能瓶颈原因的推测了(请参阅表 1)。为了验证您的推测是否正确或者在多个备选的推测之间进行筛选,您需要分析更多的信息或者在系统上运行更多的标杆测试。这里是一些可以帮助您的指导意见:

  • 区分糟糕的编码(或者是应用程序组件或者是桥接层)和瓶颈 (内部的或外部的),请查看整体的 CPU 使用情况。如果它在负载下没变化,但是整体响应时间变化了,那么就是应用程序花费了它的大多数时间来等待。
  • 仅仅是因为好像是外部资源的问题,不代表您就可以立刻把责任推到资源上。 例如,分层或联网的问题,也能造成数据库看起来很慢,虽然实际上它并不慢。或者,更简单一点,您对数据库的请求可能是不合理的(每次用户登录的时候,都要进行三个表之间的 200 万行记录合并)。应当一直把桥接层的响应时间(例如,JDBC 驱动器)和资源提供的时间或者工具提供的时间进行比较(例如,DBA Studio)。
  • 结构化的图表有助于您理解系统内部的整体交互,但是不要忘记地图并不是领地。.编码错误或者对架构意图的误解,有可能使系统的实际行为与期望的行为不同。请相信性能工作提供的实际数据,而不要相信一个声称“每个用户事务将只会发布一个 SQL 语句”这样的文档。
  • 使用 Occam 军刀。 假设您有两个备选推测,一个是:在 200 万行代码里,有一个编码糟糕的组件,直到上周这个组件才集成进来;另一个是 JVM 的即时编译器生成了糟糕的机器码,破坏了这个变量的内存完整性。除非您有具体的数据来证实,否则(我已经看到了第二种情况发生),请更详细地检查第一个假设。J2EE 系统确实容易出错,但是不要让这一点就妨碍您先测试一个更简单的假设。
  • 日志文件中没有错误不代表不存在错误。 有许多原因可以造成不在日志里写下异常;可能是因为程序员认为某件事“永远不会发生”,于是就排除了这个异常;也可能是因为某些组件可以使用故障恢复机制,所以就没有记录第一次故障。

示例诊断
  让我们来实际研究一个示例。您的 WebLogic 应用程序表现出在负载下越来越慢的症状。您加入的用户越多,系统越慢。一旦消除负载,系统就冷静下来,没有任何后遗症。您对这一主要症状进行测试,发现并得到图 2 所示的以下结果(时间测量针对的是单一典型事务的端到端完成时间)。

表2

负载(用户数) 来回用时(毫秒)
10 300
50 471
100 892
150 1067

应用程序性能在负载下越来越慢

  您形成了几个假设。也许这里的毛病是一个糟糕编码的组件,也许是后端系统的瓶颈。它可能是一个同步阻塞点。您怎样才能分清它们的不同呢?假设您还测试了应用程序服务器在负载运行期间的CPU整体使用情况,并得到了表 3 所示的结果。

表3

负载(用户数) 来回用时(毫秒) 整体 CPU 时间(%)
10 300 30
50 471 33
100 892 37
150 1067 41

问题看起来好像是“等待”瓶颈

  现在看起来,系统不是 CPU 密集型的,这就是说它的多数时间都花在等待上了。那么是它的内部(例如,同步交通阻塞)或外部(缓慢的数据库)的问题?好,让我们假设我们还稍微多搜集了一些数据,如表 4 所示。

表4

负载(用户数) 等待数据库连接的线程数量 JDBC 查询用时(毫秒)
10 2 58
50 3 115
100 3 489
150 4 612


问题是否出在一个缓慢的 SQL 语句上呢?

   现在看起来并不是内部等待数据库连接的瓶颈,相反,好像是 JDBC 查询本身的问题。JDBC 查询不仅随整体事务时间的不同而不同,而且它糟糕的性能还解释了整体性能糟糕的原因。但是,我们还不能完全确定。您仍然还有三个主要的假设要排序:是否数 据库本身慢?应用程序是否对数据库进行了不合理的请求?或者问题是不是出在应用程序和数据库之间的某个层上?您拿出数据库供应商专用的工具,从它的角度查 看响应时间。假设您看到如表 5 所示的数字。

表5

负载(用户数) JDBC 查询计时(毫秒) DB SQL 执行的时间(毫秒)
10 58 43
50 115 97
100 489 418
150 612 589

实际是数据库中的 SQL 语句慢

   如果您没有看到这条信息,那么您可能要返回 JDBC 驱动程序,期待能够发现其中的某些同步问题(请记住,CPU 没有被抑制)。幸运的是,在这个案例里,您已经把具体问题的范围缩小到数据库查询上。找出查询请求是否足够合理,需要一些相关领域的知识,需要熟悉系统, 但是在这个案例里,它也许就是发现查询把非索引字段和外键进行了比较。您和 DBA 协作,它修改索引方案,让查询变得更快,而您则找到了您的药方。 

结束语
  诊断 WebLogic J2EE 应用程序的性能瓶颈是一个艰苦的旅程。请随时保持清醒,把事实与推测分开,总是用实际的证据来确认您的推测。我希望给您带来了思考和实践问题的一些有用的想法的分类。就像调试,这仍然是一项不明确的艺术,但是深思熟虑会带您走出困境。祝您好运!

原文出处  http://www.sys-con.com/story/?storyid=43024&DE=1
 作者简介

John Bley是Wily Technology 的软件工程师。他有丰富的 Java 编程和架构经验。为了撰写本文,他总结了 Wily 的企业客户的经验,Wily负责管理复杂的 J2EE 环境。

你可能感兴趣的:(如何诊断 J2EE 系统中的性能问题)