WebObjects/Web 应用程序/部署/常见问题及故障排除
我正在开发的应用程序在调用 editingContext.saveChanges 时会冻结大约一分钟,系统输出中添加了以下消息
<WorkerThread3> <WOWorkerThread id=3 socket=Socket[addr=/xxx.xxx.xxx.xxx,port=51634,localport=51563]> Exception while sending response: java.net.SocketException: Broken pipe
奇怪的是,实际上没有抛出异常,并且更改已保存到数据库中。关于此消息的含义,有什么线索吗?
此消息意味着 woadaptor 在分配的时间内未收到应用程序的响应。此时,woapdator 会挂断应用程序,假设它已死机或过于繁忙。如果运行了多个实例,woadaptor 会将请求转发到另一个实例。对于组件操作,这会产生会话过期或无法恢复页面的错误消息。
当应用程序最终完成请求处理后,它会尝试将结果返回给 woadaptor,但会发现它已经挂断了(即关闭了套接字,断开了管道)。此时,上述异常会输出到日志中。
有几种方法可以避免这种情况
- 优化应用程序,以便更快地处理请求。
- 使用 WOLongReponsePage 处理长时间运行的请求。
- 增加 JavaMonitor 中的连接超时和接收超时值,以便 woadaptor 能够等待足够长的时间以供应用程序提供响应。
注意:如果此消息出现在其他上下文中(即不是在请求处理时间过长时),它可能只是意味着用户在浏览器中点击了停止或点击了另一个链接。
Wotaskd 使用一个名为 SpawnOfWotaskd.sh 的脚本启动新的 WOA 实例,该脚本位于 OS X 上的 /System/Library/WebObjects/JavaApplications/wotaskd.woa/Contents/Resources/SpawnOfWotaskd.sh。由于某种原因,此脚本被编写为丢弃 stderr。这意味着如果您想获得线程堆栈转储,您将无法做到。幸运的是,这是一个简单的修复。如果您编辑 SpawnOfWotaskd.sh,库存版本看起来像
#!/bin/sh $@ 1>/dev/null 2>&1 &
注意流 1 会转到 /dev/null,流 2 会写入流 1。不好。将其更改为
#!/bin/sh $@ 1>>/var/log/webobjects.err 2>&1 &
现在唯一的问题是,WebObjects 应用程序通常以 appserver 用户身份运行,这意味着它们将无法写入此文件。要解决此问题,您可以以 root 身份“touch /var/log/webobjects.err”,然后将空白文件的所有者更改为 WebObjects 应用程序运行的任何用户。
啊,是的,AWT 的乐趣。如果您接触任何 AWT 类(即使是不合理的类,如 Dimension 或 Rectangle),都会创建一个 AWT Toolkit(在静态块中),并且 AWT 会尝试连接到窗口服务器。当然,当您在开发模式下运行时,您拥有对窗口服务器的访问权限,一切都很顺利。一旦您尝试以 appserver 用户身份部署,一切都崩溃了。您有两个选择来解决此问题
- 不要使用它们。这是避免此问题最万无一失的方法。如果您不需要,请不要使用 AWT 类。显然,这通常说起来容易做起来难。
- 将 -Djava.awt.headless=true 添加到您的 VM 启动参数中。这会告诉 AWT 使用无头模式。如果您使用的是较旧版本的 Apple JVM,这仍然会导致问题(奇怪的调试语句会要求您在控制台中按一个键)。
- PJA Toolkit。这实际上是一个很酷的库。它是 AWT 工具包的纯 Java 实现。在普通系统上,您的 AWT 实现是 JNI(与您的本机窗口系统对话)。PJA 为正常 AWT 工具包尝试执行的所有功能提供纯 Java 实现。显然,它实际上并不使用窗口服务器,而是类似于带有虚拟帧缓冲区的 VNC 服务器。
- 以 root 身份运行。这通常不建议,但您可以修改 /System/Library/StartupItems/WebObjects/WebObjects 脚本,使其以 root 身份运行 WO,而不是以 appserver 用户身份运行。您将在该脚本中找到有关如何执行此操作的说明。
对于在 Mac OS X 上遇到监视器问题的人,请检查以下参数
- 检查机器在 DNS 中是否正确识别,包括反向查找。
- 验证您在监视器中添加机器时是否包含完全限定名称,例如“machine.domain.com”,而不仅仅是“machine”。使用较短的版本可能看起来有效,但实际上无效(这已由 Apple 记录)。
- 检查适配器配置 (/System/Library/WebObjects/Adaptors/Apache/apache.conf),特别是实例发现方法:这需要与 wotaskd 的命令行启动一致(请参阅 /System/Library/StartupItems/ 中的脚本)。您可能需要手动修改脚本。
- 当一切都设置好后,重新启动。它应该可以工作。
在较旧版本的 OS X 服务器中,WOtaskd 和 JavaMonitor 是使用 StartupItems 系统启动的。在较新的版本的 OS X 服务器和 WebObjects 中,Apple 则使用其新的 launchd 框架。如果您使用开发者工具安装 WebObjects,您通常只会获得 Startupitem 脚本,因此您应该按照这些说明操作。
要详细了解 launchd,您可以阅读 Apple 的文档。
WebObjects launch daemon 项目默认情况下处于禁用状态,因此您需要做的第一件事是启用它们
- cd /System/Library/LaunchDaemons
- 编辑“com.apple.womonitor.plist”和“com.apple.wotaskd.plist”
- 将 Disabled 键从“true”更改为“false”
- 保存并退出
接下来,您需要手动加载 launch daemon 项目
- 以 root 身份运行“launchctl”
- “list”并查找 com.webobjects.womonitor 和 com.webobjects.wotaskd
- 如果它们存在,您需要手动加载它们
- “load /System/Library/LaunchDaemons/com.apple.wotaskd.plist”
- “load /System/Library/LaunchDaemons/com.apple.womonitor.plist”
- 再次“list”并验证它们都出现
- “start com.webobjects.wotaskd”
- “start com.webobjects.womonitor”
- 退出 launchctl(使用 ctrl-c)
wotaskd 和 womonitor 现在都应该在运行。您可以通过访问 https://127.0.0.1:56789 来测试监视器。
如果这不起作用,请检查 /Library/WebObjects/Configuration 上的文件系统权限,它应该是 appserver:appserverusr,或者至少它们应该对此目录具有写入权限。
在终端窗口中运行以下命令将告诉您 wotaskd 无法启动的确切原因...
sudo -u appserver /System/Library/WebObjects/JavaApplications/wotaskd.woa/Contents/Resources/javawoservice.sh -appPath /System/ Library/WebObjects/JavaApplications/wotaskd.woa/wotaskd
在开发者安装和较旧的 Mac OS X 服务器安装中,WebObjects 是使用旧的 StartupItems 系统启动的。默认情况下,wotaskd 和 womonitor 处于禁用状态。幸运的是,启用它们很容易
- cd /System/Library/StartupItems/WebObjects
- 编辑“WebObjects”
- 搜索“-appPath”,您将在脚本的此区域找到四个注释掉的代码行。
- 要以 root 身份运行 WebObjects,请取消注释前两条命令(您应该看到 WOTASKD 和 WOMONITOR 行)
- 要以其他用户身份运行 WebObjects(默认情况下为 appserver 用户),请取消注释第二组代码行
- 保存并退出
- 使用两种方法之一启动 WebObjects 服务
- sudo systemstarter start "WebObjects 服务"
- ./WebObjects start
完成这些更改后,wotaskd 和 womonitor 将在重启后自动启动。
注意:不要在安装了 WO 5.3.* 的 MacOS X 10.4.* Server 机器上执行这些步骤。在该系统中,进程是作为 LaunchDeamon 启动的(如上所述)。如果你启用了启动项,系统会尝试启动两次 wotaskd,这会导致任何操作都无法正常进行。
有几种处理死锁和应用程序挂起的方法。
如果你正在使用 性能分析器 或调试器,你通常可以暂停执行并查看你的应用程序在哪个地方停止运行。
如果你使用的是 JDK 1.4,你可以向你的应用程序发送 QUIT 信号,它会转储所有活动线程。在 OS X 上,找到你的应用程序的 pid,然后执行 "kill -QUIT yourAppPID"。线程堆栈跟踪将转储到日志文件。如果你的日志中没有输出,请参阅上面的“我的 stderr 在哪里?!”以了解可能的因素。
如果你使用的是 JDK 1.5,除了可以向应用程序发送 QUIT 信号外,你还可以(以 root 或启动用户的身份)连接到它,并使用 "jstack" 应用程序通过调用 "jstack yourAppPID" 来获取线程堆栈转储。
如果看到 WOWorkerThreads 挂起等待恢复会话,通常意味着先前的请求从未正确检查回其会话的位置抛出了异常。WebObjects 中的会话访问是单线程的。导致此问题常见的案例包括从你的 awake 方法以及从 DirectAction 方法中抛出异常。
在 JDK 1.4 中,你将能够看到每个堆栈跟踪持有的对象锁列表。你可以通过查找包含两个或多个重叠锁的多个线程来查找死锁。在 JDK 1.5 中,VM 会检测死锁情况,并在线程堆栈转储中识别它们。