WebObjects/Web 应用程序/部署/调试冻结的已部署实例
本文由 Andrew Lindesay (http://www.lindesay.co.nz) 撰写于 2005 年 2 月左右。它最初以 LaTeX PDF 格式出现,并已转录到此维基。您自行承担使用本文档中包含的信息的风险。如果您认为维基标记转换过程中可能存在错误,请联系作者。
此处讨论的材料已在 MacOS-X Server 上的 WebObjects 5 和 Java 1.4 中使用。它可能适用于或不适用于旧版或新版 WebObjects 或 Java。强烈建议您首先在非生产服务器上对其进行测试。请注意,如果您的系统在互联网上公开,此技术可能存在安全问题。
Java 提供了 WebObjects 5 应用程序运行的基础。Java 有其优缺点。一个优点是轻松实现软件系统的多线程操作。这通常可以用来充分利用您的硬件资产,但也意味着您需要确保对事物进行锁定,以防止两个线程同时访问同一事物。锁定失败会导致数据损坏,或者当一个线程由于某种原因无法释放锁定时,其他线程可能会无限期地等待。
当多线程系统中“出现问题”时,获得可靠的诊断可能很困难。问题通常很难重现,可能非常零星,并且通常很少见。可能需要一系列非常具体的事件,并且需要非常精确的时间才能重现问题,并且这些问题通常难以在“实验室”中重现。在生产环境中,此类错误可能会导致应用程序实例“冻结”,并最终使实时系统无响应。
无限循环等更常见的错误也可能潜入生产系统,并导致实例无响应。
本文重点介绍了一种技术,该技术可用于确定生产环境中冻结实例内部发生了什么。特别是,此技术将为您获取实例线程的堆栈跟踪。有了这些信息,您将更有可能诊断问题并快速解决问题。
以传统方式部署的 WebObjects 系统包含程序的多个副本,它们分别运行,每个副本都承载来自用户的某些入站负载。每个“副本”都被称为一个实例。该SiteConfig.xml文件定义了实例。此配置文件位于您的 MacOS-X Server 文件系统的以下位置。
/Library/WebObjects/Configuration/SiteConfig.xml
在修改它之前,请备份SiteConfig.xml文件以防出现任何问题。
实例被修改,以便可以使用jdb调试工具远程连接到它们。需要将一些“附加参数”插入到配置中的SiteConfig.xml文件中的每个实例,以便实现这一点。这些附加参数将如文本所示插入到元素additionalArgs中。您需要为每个实例选择不同的地址 - 从 8000 到 8999 之间选择地址。这是一个 TCP/IP 端口。请注意,所有附加参数都应显示在一条连续的行上。作者在此处将其拆分以提高可读性。
...
<instanceArray type="NSArray">
<element type="NSDictionary">
<id type="NSNumber">1</id>
<port type="NSNumber">2001</port>
<cachingEnabled type="NSString">YES</cachingEnabled>
<additionalArgs type="NSString">
-Xdebug
-Xrunjdwp:transport=dt_socket,address=8121,server=y,suspend=n
</additionalArgs>
...
</element>
该元素标签将在此处重复,以包含在instanceArray标签中的所有实例。
该id标签给出实例编号,您需要记住从实例编号到地址的映射,这些映射在附加参数中。将这些信息记在一张纸上。例如,在上面,可以看出实例 1 映射到地址 8121。
现在重新启动您的实例。
实例列在 JavaMonitor 中。下面显示了一个屏幕截图,实例编号用红色圆圈标记。您首先需要确定哪个实例已冻结。
获得实例编号后,使用您的实例到地址映射(来自设置),识别您要连接到的地址。
使用jdbjava 环境附带的命令行工具连接到实例并调试它。为此,请在应用程序服务器上输入以下形式的命令。
fooserver$ jdb -attach 8121
如果您要调试远程机器,可以使用以下命令。
foodev$ jdb -attach woserverhost:8121
这可能在您的 WebObjects 应用程序服务器上没有安装jdb工具的情况下很有用,因此您需要从安装了jdb工具的主机运行 jdb 工具。
您现在将使用 java 调试器。有大量命令可以帮助您使用调试的 java 系统,但本文只关注获取线程堆栈跟踪。发出命令suspend以冻结所有线程,以便可以将其转储,然后发出命令where all以获取所有线程的堆栈跟踪。最后,当您希望再次恢复线程时,请发出命令resume。建议您在获得所需信息后立即退出jdb环境。
下面显示了堆栈跟踪的示例。您会注意到 java 类名和源代码行号位于堆栈中特定条目末尾。在这里,我们可以看到名为“WorkerThread103”的线程卡住了,试图从会话存储中获取会话。在这种情况下,另一个线程很可能拥有会话存储的锁定,并且没有释放该锁定。
WorkerThread103: [1] java.lang.Object.wait (native method) [2] java.lang.Object.wait (Object.java:429) [3] com.webobjects.appserver.WOSessionStore.checkOutSessionWithID (WOSessionStore.java:207) [4] com.webobjects.appserver.WOApplication.restoreSessionWithID (WOApplication.java:1,546) [5] com.webobjects.appserver._private.WOComponentRequestHandler._dispatchWithPreparedApplication (WOComponentRequestHandler.java:314) [6] com.webobjects.appserver._private.WOComponentRequestHandler._handleRequest (WOComponentRequestHandler.java:358) [7] com.webobjects.appserver._private.WOComponentRequestHandler.handleRequest (WOComponentRequestHandler.java:432) [8] com.webobjects.appserver.WOApplication.dispatchRequest (WOApplication.java:1,306) [9] nz.co.orcon.osm.webobjects.Application.dispatchRequest (Application.java:428) [10] com.webobjects.appserver._private.WOWorkerThread.runOnce (WOWorkerThread.java:173) [11] com.webobjects.appserver._private.WOWorkerThread.run (WOWorkerThread.java:254) [12] java.lang.Thread.run (Thread.java:552) WorkerThread101: [1] java.net.PlainSocketImpl.accept (PlainSocketImpl.java:351) [2] java.net.ServerSocket.implAccept (ServerSocket.java:448) [3] java.net.ServerSocket.accept (ServerSocket.java:419) [4] com.webobjects.appserver._private.WOWorkerThread.run (WOWorkerThread.java:238) [5] java.lang.Thread.run (Thread.java:552) ...
如果您查看其他线程同时在做什么,您很有可能确定哪个区域可能存在错误。至少,您可以弄清楚应用程序的哪个部分存在错误。
尽管这种方法很简单,但它提供了一种方法,可以使您了解冻结实例内部发生了什么,而不是进行繁琐的猜测游戏。