WebObjects/Web 应用程序/部署/Tomcat 部署
本文由 Andrew Lindesay 于 2005 年 5 月左右撰写(http://www.lindesay.co.nz)。它最初以 LaTeX PDF 形式出现,现已转录到此 Wiki。您使用此文档中包含的信息需自行承担风险。如果您认为 Wiki 标记的转换过程中可能存在错误,请联系作者。
从 WebObjects 5.2 开始,就可以从 WebObjects 应用程序项目中获得一个构建产品,该产品可以部署到 J2EE servlet 容器中。本文展示了如何将 WebObjects 5.2 应用程序部署到 Tomcat 环境中,并实现与“本机”WebObjects 部署非常相似的拓扑结构。
本文最初是在假设 Tomcat 5 部署的情况下编写的,但在使用 Web 服务和 AXIS 遇到一些困难后,我修改了本文以适应 Tomcat 3 部署。本文涵盖了这两种情况。
本文假设以下内容
- WebObjects 5.2(可能与较新版本也能正常工作)
- Java 1.4
- 最新版本的 Tomcat 版本 5(撰写本文时为 5.5.12)或 3(撰写本文时为 3.3.2)
- 某种类型的 UNIX 部署。
- 读者对 servlet 技术背后的概念有所了解。
- 读者熟悉标准的 WebObjects 部署拓扑结构,将被称为wotaskd部署。
为了便于说明本文的内容,假设 Tomcat 已安装在本地磁盘上的名为$TOMCATDIR的目录中。还假设您将拥有另一个包含配置和运行实例所需文件的目录,名为$INSTDIR。还假设您将拥有一个名为$JKDIR的目录,其中包含 Tomcat Apache 适配器。在本文中,一些配置文件需要显示路径,这些路径在下表中列出。
$TOMCATDIR | /opt/tomcat |
$INSTDIR | /opt/fooapp |
$JKDIR | /opt/modjk |
实际上,这些目录可以位于任何位置。
本文的目的是展示 WebObjects 应用程序可以部署到 servlet 容器中,并保留 WebObjects 部署拓扑结构的一些理想属性。下面列出了一些这些属性。
- 在多个硬件节点上进行集群,以防止单个硬件故障事件导致系统停机。
- 在每个硬件节点上的多个虚拟机实例上进行集群,以防止单个软件故障事件导致停机。
- 能够有效利用低成本服务器硬件,而不是鼓励使用大型昂贵服务器。
- 最大限度地利用每个虚拟机中可用的内存作为缓存,以最大程度地减少数据库流量并降低对数据库服务器的压力。
- 能够使会话“粘滞”到给定的虚拟机实例,同时通过单个 Web 服务器前端适配器进行多路复用。
- 能够将请求负载均衡到处于运行状态的实例。
下面显示了标准的 WebObjects 部署拓扑结构以及使用 Tomcat 所要实现的目标。典型的 J2EE 部署可能具有与之不同的拓扑结构。
文件:Wo-tomcat-deploy-topology.gif
Apple 为 WebObjects 提供的文档详细介绍了如何从 WebObjects 应用程序项目中创建 servlet 构建产品。这里不再赘述,但下面简要概述一下该过程。
- 将JavaWOJSPServlet.framework框架包含在您的项目中。
- 在构建设置中,将SERVLET_SINGLE_DIR_DEPLOY值设置为YES以创建最简便的 servlet 部署形式。
- 编辑SERVLET_DEPLOY_LICENSE以包含您的有效部署许可证密钥(如果您需要使用 WebObjects 版本的密钥)。
- 编辑SERVLET_WEBAPPS_DIR指向$INSTDIR/webapps/或您想要放置构建产品的某个位置。
现在,当您选择Deployment构建时,您还会获得 servlet 的组装。最终结果是一个类似于下图所示的目录结构。
文件:Wo-tomcat-ssd-filelayout-3.gif
许多属性列表文件(通常称为 plist 文件)在顶部都有一个文档类型。这可能指的是 MacOS-X 机器上的文件或 Apple 服务器上的文件。无论哪种情况,这都会导致不在 MacOS-X 服务器上的部署出现问题。以下脚本可以运行,并将WEB-INF文件夹作为参数,以删除这些文件。WebObjects 应用程序在没有这些信息的情况下也能正常运行。此脚本可以轻松地作为您 WebObjects 项目构建过程中的一个步骤进行整合。
# [apl 3.may.2006] # This will remove any DOCTYPE's from the top of plists so that # they do not attempt to validate the DTD which is either # extracted from /System or the internet over HTTP. if [ -z $1 ]; then echo "syntax: stripdocype.sh <directory>" exit 1 fi for PLISTFILE in `find $1 -name *.plist` do sed \ '/<!DOCTYPE [^>]*>/s/.*//' \ $PLISTFILE \ > /tmp/remove-plist-temp cp /tmp/remove-plist-temp $PLISTFILE done
具有 servlet 支持的 WebObjects 项目在其文件中有一个名为web.xml.template的文件。默认情况下,此文件位于您的项目的/Resources/Servlet Resources/WEB-INF目录中。该web.xml.template文件用作创建web.xml的模板,也称为servlet 部署描述符。此部署描述符用于将设置传达给应用程序以及应用程序运行的 servlet 容器。本节介绍对该文件的常见更改,以及在应用程序运行在 servlet 容器中时如何进行一般性配置的讨论。
一些 WebObjects 工程师使用setConnectionDictionary(...)方法在模型上设置模型的 JDBC 数据库连接参数。但是,servlet 容器有自己的数据源机制来提供数据库信息,这些信息将覆盖设置到模型中的任何连接字典信息。如果您不希望这种情况发生,并且希望您的setConnectionDictionary(...)生效,请注释掉标题为jdbc/DefaultDataSource的resource-ref项,该项位于web.xml.template文件中。
要阻止 Web 服务器资源(图像、CSS 文件和其他静态数据)从 Java 环境中提供服务,您需要将名为WOAppMode的context-param配置为部署项,该项位于web.xml.template文件中。
此配置区域涵盖以下虚构示例等项目;
- 系统故障时联系人的电子邮件地址。
- 轮询某些资源的频率。
- 数据库的可选连接信息。
- 新西兰的 GST 税率。
换句话说,这些是应用程序特定的配置值。在 servlet 容器中配置应用程序特定参数的一种方法是将您的配置加载到env-entry-s 在你的web.xml文件中。以下是一个此类条目的示例;
<env-entry> <env-entry-name>foo/nz.co.foo.FooAppMailFrom</env-entry-name> <env-entry-value>[email protected]</env-entry-value> <env-entry-type>java.lang.String</env-entry-type> </env-entry>
您可以使用如下所示的代码在应用程序中检索这些值。请参阅 LEWOStuff 的 LEConfig 类,以及 Lindesay Electric 的 WebObjects 框架以获取示例。LEWOStuff 还附带一个工具,可以帮助将标准 java 属性文件加载到 servlet 部署描述符中。
import javax.naming.*; // ...later in the same class... Object valueO = null; try { InitialContext context = new InitialContext(); valueO = context.lookup("java:comp/env/foo/nz.co.foo.FooAppMailFrom"); } catch(javax.naming.NamingException ne) { /* handle gracefully */ }
将这些设置应用于web.xml文件可能是最简单的,作为更自动化的构建或部署过程的一部分。
将您的应用程序 servlet 构建产品放在$INSTDIR,以便存在以下路径。
$INSTDIR/webapps/FooApp/WEB-INF
您需要为要拥有的每个实例创建一个 tomcat 配置文件。此处遵循使用实例编号后跟小写字母 "i" 的模式。将第一个服务器配置文件放在以下位置。
$INSTDIR/server_i1.xml
以下是如何创建此文件的一个示例。此处不涉及各个设置,因为读者应该阅读 tomcat 文档以了解这些设置的具体含义。
<Server port="7071" shutdown="SHUTDOWN"> <Service name="Catalina"> <Connector port="8081" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" /> <Connector port="9091" enableLookups="false" protocol="AJP/1.3" /> <Engine name="i1" defaultHost="appserver1.foo.co.nz" jvmRoute="i1"> <Host name="appserver1.foo.co.nz" appBase="/home/fooapp/webapps" unpackWARs="true" autoDeploy="false" xmlValidation="false" xmlNamespaceAware="false"> <Context cookies="false" docBase="FooApp" path="FooApp" reloadable="false"> <Manager distributable="false" /> </Context> </Host> </Engine> </Service> </Server>
<?xml version="1.0" encoding="ISO-8859-1"?> <Server> <ContextManager workDir="work"> <LoaderInterceptor11 useApplicationLoader="true" /> <AutoDeploy source="modules" target="modules" redeploy="true" /> <AutoWebApp dir="modules" host="DEFAULT" trusted="true"/> <AutoWebApp dir="/home/fooapp/webapps" trusted="true" reloadable="false" /> <SimpleMapper1 /> <SessionExpirer checkInterval="60" /> <SessionIdGenerator randomClass="java.security.SecureRandom" /> <WebXmlReader validate="false" /> <ErrorHandler showDebugInfo="true" /> <Jdk12Interceptor /> <LoadOnStartupInterceptor /> <Servlet22Interceptor /> <SessionId cookiesFirst="false" noCookies="true" /> <SimpleSessionStore maxActiveSessions="256" /> <Http10Connector port="8081" secure="false" /> <Ajp13Connector port="9091" tomcatAuthentication="false" shutdownEnable="true" /> </ContextManager> </Server>
假设在本示例部署中将有三个实例,则应复制整个文件并修改两次以用于另外两个实例。对于其他实例的服务器配置文件,请通过修改数字部分来更改 "i1"(仅限 Tomcat 5),并通过使最后一位数字为实例编号来更改端口号。例如,端口7071, 8081和9091在此处使用。对于 "i2",请使用7072, 8082和9092.
现在您应该拥有三个文件,名为server_i1.xml, server_i2.xml和server_i3.xml在目录中$INSTDIR.
要启动实例,请执行以下命令。您应该针对每个服务器配置文件执行此命令。在启动实例之前,应正确设置$JAVA_HOMEshell 环境变量。
$TOMCATDIR/bin/startup.sh -config $INSTDIR/server_i1.xml
$TOMCATDIR/bin/startup -config $INSTDIR/server_i1.xml -home $TOMCATDIR
要将 java 环境变量传递给您的应用程序,请在启动 Tomcat 3 环境之前设置TOMCAT_OPTSshell 环境变量。以下是一个示例;
TOMCAT_OPTS=-Dabc=xyz export TOMCAT_OPTS
您现在可以使用以下 URL 查看您的实例是否已启动。
http://appserver1.foo.co.nz:8081/FooApp/WebObjects/FooApp.woa
请注意,AJP(这是 apache 适配器和各个 Tomcat 实例之间的协议)和常规 HTTP 引擎都访问同一个运行应用程序。这意味着直接通过 HTTP 对应用程序进行的测试实际上是在测试通过 AJP 访问的应用程序。此行为提供了一种通过直接 HTTP 监控 Tomcat 的特定实例的机会。
检查每个实例。
在 Tomcat 5 下,检查以下位置的日志文件$TOMCATHOME/logs/catalina.out如果您无法访问您的实例。
要关闭实例,请执行以下命令。
$TOMCATDIR/bin/shutdown.sh -config $INSTDIR/server_i1.xml
也可以使用 UNIXtelnet命令连接到服务器配置文件中描述的端口Server标签,并输入shutdown属性中提供的单词以关闭 Tomcat 实例。
TOMCATDIR/bin/shutdown -ajp13 -port 9091
最终部署通常涉及 apache 将传入请求转发到实例并处理实例关闭的情况,以及在当前运行的实例上进行负载均衡。此工作由mod_jk完成,该模块是 Tomcat 组为 apache 编写的模块。此模块使用称为AJP的协议与 tomcat 实例通信。此协议包含所有必要的检查实例是否可操作以及将请求中继到实例的信息。假设mod_jkapache 模块的配置和二进制文件位于$JKDIR.
下载和安装说明mod_jk可以从 Tomcat 网站获取。mod_jk.so二进制文件应位于以下路径。
$JKDIR/mod_jk.so
在系统 Apache httpd 配置文件中添加一行,如下所示
Include /opt/modjk/apache.conf
编辑此文件使其看起来像这样
LoadModule jk_module /opt/modjk/mod_jk.so AddModule mod_jk.c JkLogFile /opt/modjk/mod_jk.log JkLogLevel info JkWorkerProperty worker.list=i1,i2,i3,loadbalancer JkWorkerProperty worker.i1.type=ajp13 JkWorkerProperty worker.i1.port=9091 JkWorkerProperty worker.i1.host=appserver1.foo.co.nz JkWorkerProperty worker.i1.lbfactor=1 JkWorkerProperty worker.i2.type=ajp13 JkWorkerProperty worker.i2.port=9092 JkWorkerProperty worker.i2.host=appserver1.foo.co.nz JkWorkerProperty worker.i2.lbfactor=1 JkWorkerProperty worker.i3.type=ajp13 JkWorkerProperty worker.i3.port=9093 JkWorkerProperty worker.i3.host=appserver1.foo.co.nz JkWorkerProperty worker.i3.lbfactor=1 JkWorkerProperty worker.loadbalancer.type=lb JkWorkerProperty worker.loadbalancer.sticky_session=1 JkWorkerProperty worker.loadbalancer.local_worker_only=1 JkWorkerProperty worker.loadbalancer.balance_workers=i1,i2,i3 JkMount /FooApp/* loadbalancer
同样,本文档不会详细介绍确切的设置,但它应该为设置此文件以实现 Tomcat 下的多实例部署提供一个简单的指南。
现在使用以下命令重启 Apache
sudo apachectl restart
现在你可以使用类似于以下的 URL 来测试你的应用程序。
http://www.foo.co.nz/FooApp/WebObjects/FooApp.woa
你设置的所有三个实例都应该接收一些入站请求。
mod_jk确保已启动会话的请求将被定向到产生会话的正确实例。此行为称为“粘性会话”。似乎无法从 servlet 容器以其他方式指定实例。