UML软件工程组织

如何诊断 J2EE 系统中的性能问题
作者:John Bley 来源:互联网

现在要求您诊断 WebLogic J2EE 应用程序中的性能问题。因为 Java 系统是如此复杂,所以诊断 WebLogic J2EE 应用程序中的性能问题就有点像诊断疑难杂症。

 为了正确地找到问题所在,您需要对症状有全面了解,要做好准备进行大量研究工作,最后您需要制定正确的治疗方案。本文讨论了 J2EE 应用程序性能问题的一些最常见类型和它们产生的原因,以及如何正确地诊断和消除它们的推荐指导原则。

症状

 WebLogic 应用程序性能问题的症状是什么?您看到的症状可以指导您在所有可能的问题中进行搜索。请准备一个笔记本并开始向人们调查。试着把对问题根本原因的推测和假设与系统行为的实际证据分离。下面是一个常见症状集的列表:

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

 对于 WebLogic 应用程序的某个具体应用模式来说,没有既定的公式可以用来推导出它的性能(在严格的实时工程当中,速度单调分析这类技术确实可以做这项工作,但是在本文里还是让我们忘记它吧)。网络上是否存在另外一个系统正在密集使用一个共享的后端服务,对于实际产生的性能有很大影响。性能也许还取决于 JDBC 驱动程序版本和数据库的正确匹配。也许开发人员三年前写的一些代码恰巧在这个时候才出现特定类型的异常,而您急切需要的解决问题回馈,却恰恰包含在这个异常里。

 从本质上说,典型业务系统的性能是由成千上万交互的变量和决策共同作用的结果。就像人体一样,有太多连锁着的部分和过程,所以很难理解系统的整体。因此我们进行了简化,并求助于拱形模式。

疾病

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

表1
 
 

测量关键的统计指标

 当您负责诊断问题的时候,您应当能够跟踪关于您的 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
 

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

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

表3
 

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

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

表4
 

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

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

表5
 

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

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

结束语

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

 

版权所有:UML软件工程组织