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>bar@foo.co.nz</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 容器以其他方式指定例項。