我與TOMCAT不得不說的二三事 - 中國WEB開發者網絡 (http://www.webasp.net) -- 技術教程 (http://www.webasp.net/article/) --- 我與TOMCAT不得不說的二三事 (http://www.webasp.net/article/18/17643.htm) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -- 作者:DarkXie -- 發佈日期: 2005-04-18 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
謹以此文送給所有正在使用TOMCAT或者打算使用的人們,向TOMCAT的所有開發人員致敬! 一、小貓TOMCAT其實很可愛 2003年底,我換公司了,同樣也換了WEBAPP,TOMCAT出現在我的面前(以前使用weblogic),我有點茫然,免費的東西真的能用的好麼?擔心ING……(其實是在火星呆太久)出門一打聽,原來此貓出自名門-jakarta項目,2001年度最具創新的java產品(Most Innovative Java Product),又有JAVA的老大SUN的力捧(官方推薦的servlet和jsp容器),以後就靠它吃飯了。不說二話,搞起來先: 1、 安裝 TOMCAT最新版本是5.0.29(http://jakarta.apache.org/site/binindex.cgi) 如果在WINDOWS下它可以自動找到你的JDK或者set JAVA_HOME=c:/jdk 在LINUX下需要先解壓,然後設置JAVA_HOME export JAVA_HOME=/usr/local/jdk 2、 RUN 設置完畢後就可以運行tomcat服務器了,進入tomcat的bin目錄,WINDOWS下用startup啟動tomcat,linux下用startup.sh,相應的關閉tomcat的命令為shutdown和shutdown.sh。 啟動服務後在瀏覽器裡輸入http://localhost:8080/來測試一下 3、 目錄結構 Bin:存放啟動和關閉tomcat腳本。 Conf:包含不同的配置文件,server.xml(Tomcat的主要配置文件)。 Work:存放jsp編譯後產生的class文件。 Webapp:存放應用程序示例,以後你要部署的應用程序也要放到此目錄。 Logs:存放日誌文件 Comm./server/shared:這三個文件夾下的LIB文件夾放jar文件。 1、 配置server.xml文件 沒有什麼好說的,看TOMCAT的文檔比較有用,這裡提供一些主要的東西吧。
2、 管理 TOMCAT管理能力很強大,進入http://localhost:8080/,自己慢慢管吧。實踐出真知,我喜歡這樣搞: ^_^,一切盡在掌握http://localhost:8080/manager/html 。
一、讓數據庫連接池轉起來 作為一個J2EE程序員大家手上可能會有現成的JDBC 數據庫連接池,其實這沒有太大的必要,因為象weblogic……企業級WEBAPP都有自己的連接池,大家不要費力直接使用吧,效率也很不錯,再也不用羨慕.NET的ADO了(以前作MS從來不擔心數據連接,ADO確實用起來很爽),如果想實現一個 JDBC connection pool 的注意事項有: 1. 有一個簡單的函數從連接池中得到一個 Connection。 2. close 函數必須將 connection 放回 數據庫連接池。 3. 當數據庫連接池中沒有空閒的 connection, 數據庫連接池必須能夠自動增加 connection 個數。 4. 當數據庫連接池中的 connection 個數在某一個特別的時間變得很大,但是以後很長時間只用其中一小部分,應該可以自動將多餘的 connection 關閉掉。 5. 如果可能,應該提供debug 信息報告沒有關閉的 new Connection 。 網上有各種各樣的連接池代碼,抄過來改改吧,嘿嘿∼
這裡介紹如何配置TOMCAT的連接池,以SQLSERVER為例: 步驟1:安裝SQLSERVER的JDBC驅動 SQLSERVER的JDBC驅動其實就是三個JAR文件,msbase.jar/mssqlserver.jar/msutil.jar,將這三個文件拷貝到你的/tomcat_home/common/lib目錄下去就可以了。 步驟2:修改server.xml文件 具體代碼如下: <Context path="test" docBase="F:\yourroot" debug="5" reloadable="true" crossContext="true"> <Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_DBTest_log." suffix=".txt" timestamp="true"/> <Resource name="jdbc/SqlServerDB" auth="Container" type="javax.sql.DataSource"/> <ResourceParams name="jdbc/SqlServerDB"> <parameter> <name>factory</name> <value>org.apache.commons.dbcp.BasicDataSourceFactory</value> </parameter> <!-- Maximum number of dB connections in pool. Make sure you configure your mysqld max_connections large enough to handle all of your db connections. Set to 0 for no limit.--> <parameter> <name>maxActive</name> <value>50</value> </parameter> <!-- Maximum number of idle dB connections to retain in pool. Set to 0 for no limit.--> <parameter> <name>maxIdle</name> <value>20</value> </parameter> <!-- Maximum time to wait for a dB connection to become available in ms, in this example 0.5 seconds. An Exception is thrown if this timeout is exceeded. Set to -1 to wait indefinitely. --> <parameter> <name>maxWait</name> <value>500</value> </parameter> <!-- msSQL dB username and password for dB connections --> <parameter> <name>username</name> <value>sa</value> </parameter>
<parameter> <name>password</name> <value>sa</value> </parameter> <!-- Class name for SQLServer2000 JDBC driver --> <parameter> <name>driverClassName</name> <value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value> </parameter> <!-- The JDBC connection url for connecting to your MS SQL Server dB.The autoReconnect=true argument to the url makes sure that the mm.Sql Server JDBC Driver will automatically reconnect if mysqld closed the connection. mysqld by default closes idle connections after 8 hours.--> <parameter> <name>url</name> <value>jdbc:microsoft:sqlserver://10.0.254.11:1433;databaseName=yourdb</value> <!--must use & not use & --> </parameter> </ResourceParams> </Context> 步驟三:程序調用 package dbmanage;
import java.sql.CallableStatement; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource;
import util.smartDateFormat;
public class dbManager { /************************************ * @param static private boolean VERBOSE ; * @param Statement theStatement; * @param PreparedStatement thePstmt; * @param Connection theConnection; ************************************/ final static private boolean VERBOSE = true; //打印控制台控制
//static Logger logger = Logger.getLogger(dbManager.class.getName()); private Context initCtx = null; private Context ctx = null; private DataSource ds = null;
private long timeout = 5000;
private Statement theStatement = null; private PreparedStatement thePstmt = null;
/************************************ * 初試化initCtx * 取得數據源對像 ************************************/ public dbManager() { try { initCtx = new InitialContext(); //init context,read config web.xml if (initCtx == null) { throw new Exception("Initial Failed!"); } ctx = (Context) initCtx.lookup("java:comp/env"); //find "jdbc/SqlServerDB" object this configruation in the SERVER.XML of Tomcat if (ctx != null) { ds = (DataSource) ctx.lookup("jdbc/SqlServerDB"); } if (ds == null) { throw new Exception("Look up DataSource Failed!"); } } catch (Exception e) { log(e, "Can』t get the Context!"); } }
/************************************ * get Connection * @return Connection ************************************/ public synchronized Connection getConnection() { //get connection and set to delay time long startTime = new java.util.Date().getTime(); Connection con = null; while (con == null) { con = newConnection(); if (con != null) { //log("Create New Connection!"); break; } try { log("連接超時,重新連接,等待" + timeout + "ms"); wait(timeout); } catch (InterruptedException e) { log(e, "連接超時!"); } if ( (new java.util.Date().getTime() - startTime) >= timeout) { log("Connection timeout!"); break; } } return con; }
private Connection newConnection() { Connection con = null; try { con = ds.getConnection(); if (con == null) { throw new Exception("Create Connection Failed!"); } } catch (Exception e) { log("Create Connection Failed!"); System.out.println(e.getMessage()); } return con; }
/************************************ * release the connection * @param conn Connection * @param stmt Statement * @param pstmt PreparedStatement ************************************/ public synchronized void freeConnection(Connection conn, Statement stmt, PreparedStatement pstmt) { try { //close Statement if (stmt != null) { stmt.close(); stmt = null; //log("Close Statement......"); } //close PreparedStatement if (pstmt != null) { pstmt.close(); pstmt = null; //log("Close PreparedStatement......"); } } catch (Exception e) { System.out.println(e.getMessage()); } try { //close Connection if (conn != null) { conn.close(); conn = null; //log("Close Connection......"); } } catch (SQLException e) { log(e, "釋放資源出錯!"); } }
/************************************ * write log file. * @param s String ************************************/ private void log(String s) { if (VERBOSE) { System.out.println(new java.util.Date() + ":" + s); //logger.info(new java.util.Date()+s); } }
/************************************ * write log file. * @param ex Object ************************************/ private void logerr(Object ex) { if (VERBOSE) { //System.out.println(new java.util.Date()+":"+s); //logger.error(ex); } }
/************************************ * write log file. * @param e Throwable * @param msg String ************************************/ private void log(Throwable e, String msg) { System.out.println(new java.util.Date() + ": " + msg); //logger.info(new java.util.Date() + ": " + msg, e); } …… }
OK,你現在可以方便的使用連接池了,想要一個得一個,記得要釋放哦,連接池的數量總是有限的。
二、中文問題照樣很簡單 每個國家(或區域)都規定了計算機信息交換用的字符編碼集,如美國的擴展 ASCII碼, 中國的 GB2312-80,日本的 JIS 等,作為該國家/區域內信息處理的基礎,有著統一編碼的重要作用。字符編碼集按長度分為 SBCS(單字節字符集),DBCS(雙字節字符集)兩大類。早期的軟件(尤其是操作系統),為了解決本地字符信息的計算機處理,出現了各種本地化版本(L10N),為了區分,引進了 LANG, Codepage 等概念。但是由於各個本地字符集代碼範圍重疊,相互間信息交換困難;軟件各個本地化版本獨立維護成本較高。因此有必要將本地化工作中的共性抽取出來,作一致處理,將特別的本地化處理內容降低到最少。這也就是所謂的國際化(I18N)。各種語言信息被進一步規範為 Locale 信息。處理的底層字符集變成了幾乎包含了所有字形的 Unicode。 現在大部分具有國際化特徵的軟件核心字符處理都是以 Unicode 為基礎的,在軟件運行時根據當時的 Locale/Lang/Codepage 設置確定相應的本地字符編碼設置,並依此處理本地字符。在處理過程中需要實現 Unicode 和本地字符集的相互轉換,甚或以 Unicode 為中間的兩個不同本地字符集的相互轉換。這種方式在網絡環境下被進一步延伸,任何網絡兩端的字符信息也需要根據字符集的設置轉換成可接受的內容。 Java 語言內部是用 Unicode 表示字符的,遵守 Unicode V2.0。Java 程序無論是從/往文件系統以字符流讀/寫文件,還是往 URL 連接寫 HTML 信息,或從 URL 連接讀取參數值,都會有字符編碼的轉換。這樣做雖然增加了編程的複雜度,容易引起混淆,但卻是符合國際化的思想的。從理論上來說,這些根據字符集設置而進行的字符轉換不應該產生太多問題。而事實是由於應用程序的實際運行環境不同,Unicode 和各個本地字符集的補充、完善,以及系統或應用程序實現的不規範,轉碼時出現的問題時時困擾著程序員和用戶。 其實解決 JAVA 程序中的漢字編碼問題的方法往往很簡單,但理解其背後的原因,定位問題,還需要瞭解現有的漢字編碼和編碼轉換。相信這樣的東西大家都見過了 new String(request.getParameter("test").getBytes("iso-8859-1"),"GBK") 但這樣的代碼相信不是一個解決的辦法,這樣會增加程序的複雜度,寫數據庫,提交表單,URL中傳中文參數,到處都是中文問題!作為一個連走路都要算計最短距離的懶人,當然不願天天叨念著new String(request.getParameter("test").getBytes("iso-8859-1"),"GBK"),然漢戰戰兢兢的處理各種字符轉換的問題,我跋山涉水,翻山越嶺,終於找到了完美的解決方式,在TOMCAT中只需要簡單的配置,引入2個文件就可以輕鬆搞定。 前提條件,每個頁面使用 <%@ page contentType="text/html; charset=GBK" language="java" import="java.sql.*" errorPage="" %> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> 地球人都知道的東西。 步驟1:添加過濾器 在TOMCAT中找到這2個文件RequestDumperFilter.java,SetCharacterEncodingFilter.java,他們位於D:\Tomcat5.0.27\webapps\jsp-examples\WEB-INF\classes\filters,加到你的工程文件裡去,編譯他們。 步驟2:配置WEB.XML 在web.xml裡加入這一段 …… <filter> <filter-name>Set Character Encoding</filter-name> <filter-class>filters.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>GBK</param-value> </init-param> </filter> <filter-mapping> <filter-name>Set Character Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> …… 看到沒有?這樣你就不用寫那些麻煩的轉換代碼了,當然這樣還不足以解決問題。 步驟3:修改server.xml 在server.xml修改2個地方 <Connector port="8080" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true" URIEncoding=』GBK』/> <Connector className="org.apache.coyote.tomcat5.CoyoteConnector" port="8009" minProcessors="5" maxProcessors="75" enableLookups="true" redirectPort="8443" acceptCount="10" debug="0" connectionTimeout="0" useURIValidationHack="false" protocol="AJP/1.3" protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler" URIEncoding=』GBK』/> OK,搞定!
三、APACHE和TOMCAT他們倆關係非同一般 Apache和tomcat都是很優秀的軟件,更可貴的是它們是免費的。其實他們2個都是jakarta項目的重要組成部分。按輩分來講,TOMCAT是APACHE的兒子,APACHE的專長是解析靜態文件,CGI,PHP……圖片……,兒子當然不能搶了老爹的飯碗,所以TOMCAT只有在J2EE這個上面發憤圖強,其實TOMCAT並非不能幹他老爹的活,只是穩定性差點而已(偶沒有明顯的感覺,可能是商業炒作吧),現在大家明白為什麼把他們2個扯一起了吧,上陣還靠父子兵呢∼ 把2個傢伙整一起有大致有2種方法,一種是利用mod_jk2.so,一種是利用mod_jk_1.2.5_2.0.47.dll。這2個東東叫聯接器(TOMCAT就是通過這傢伙與apache勾搭上的) 1、 利用mod_jk_1.2.5_2.0.47.dll在WINDOWS下整合 步驟1:準備材料 apache2.0.52 http://apache.te8.com/dist/httpd/binaries/win32/apache_2.0.52-win32-x86-no_ssl.msi tomcat5.0.27 http://apache.linuxforum.net/dist/jakarta/tomcat-5/v5.0.19/bin/jakarta-tomcat-5.0.27.exe JDK(這個不用說了吧^_^) mod_jk_1.2.5_2.0.47.dll(關鍵是這個東東啊,找了我N久),據說在下面連接可以下到,最後在我同事那找到的。 安裝apache\ tomcat\JDK。
步驟2:安裝後設置環境變量 設置我的電腦\屬性\高級\環境變量\新建系統變量 變量名:JAVA_HOME 變量值:C:\JBuilderX\jdk1.4 (指向JDK的實際安裝路徑);TOMCAT_HMOM 變量值:Tomcat5.0.27;lasspath 編輯變量值中加上 ……;%JAVA_HOME%\bin;%JAVA_HOME%\lib;%TOMCAT_HOME%\bin;.; 測試一下,訪問http://localhost和http://localhost:8080,默認安裝是不會有什麼錯誤的^_^ 把連接器mod_jk_1.2.5_2.0.47.dll COPY到D:\Apache2\modules\下。
步驟3:apache配置 在d:\Apache2\conf下找到httpd.conf,找到DirectoryIndex,在index.html後添加index.jsp;查找「listen」用於本機測試時:Listen 127.0.0.1:80,我的是這樣設置的Listen *:80 查找AddDefaultCharset設置為AddDefaultCharset off,這樣APACHE將以你頁面定義的字符集解析頁面。 在最後添加如下代碼: <VirtualHost *:80> #localhost為本機,你可用本機ip ServerAdmin darkxie@hotmail.com #你的mail地址 DocumentRoot F:/uutang/uutang #你的項目組根目錄 ServerName dark #你的服務名,若你的機器有域名,設為域名 ErrorLog logs/ErrorLog.txt #錯誤日誌 CustomLog logs/CustomLog.txt common #訪問日誌 JkMount /servlet/* ajp13 #讓Apache支持對servlet傳送,用以Tomcat解析 JkMount /*.jsp ajp13 #讓Apache支持對jsp傳送,用以Tomcat解析 JkMount /*.do ajp13 #讓Apache支持對struts的action傳送,用以Tomcat解析 </VirtualHost> LoadModule jk_module modules/mod_jk_1.2.5_2.0.47.dll JkWorkersFile "D:/Tomcat5.0.27/conf/workers.properties" JkLogFile "D:/Tomcat5.0.27/logs/mod_jk2.log" JkLogLevel info
步驟4:tomcat配置 在d:\Tomcat5\conf下新建一個workers.properties文件 .內容如下: workers.tomcat_home=d:\Tomcat5 #讓mod_jk模塊知道Tomcat workers.java_home=d:\jdk1.3 #讓mod_jk模塊知道j2sdk ps=\ worker.list=ajp13 #模塊版本,現有ajp13了,不要修改 worker.ajp13.port=8009 #工作端口,若沒佔用則不用修改 worker.ajp13.host=localhost #主機,若上面的Apache主機不為localhost,作相應修改 worker.ajp13.type=ajp13 #類型 worker.ajp13.lbfactor=1 #代理數,不用修改 修改TOMCAT的server.xml文件: <!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 --> <Connector className="org.apache.coyote.tomcat5.CoyoteConnector" port="8009" minProcessors="5" maxProcessors="75" enableLookups="true" redirectPort="8443" acceptCount="10" debug="0" connectionTimeout="0" useURIValidationHack="false" protocol="AJP/1.3" protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler" URIEncoding=』GBK』/> 讓TOMCAT知道ajp13協議,apache和tomcat倆父子間靠這個協議溝通。 測試一下,訪問http://localhost和http://localhost:8080,看到相同的頁面沒有?細心點,其實很簡單,看看E文的幫助,搞定不成問題。
2、 利用mod_jk2.so(也叫JK2)整合 jk2是一個jakarta-tomcat-connectors-jk2.0.4-win32-apache2.0.49.zip文件,主要用的是其中的mod_jk2.so。其實利用mod_jk2.so整合和利用mod_jk_1.2.5_2.0.47.dll整合大同小異,只是換了個聯接器而已,現在一步一步整起來∼ 步驟1:沒有多說的,安裝好TOMCAT和APACHE 下載jakarta-tomcat-connectors-jk2.0.4-win32-apache2.0.49.zip,解壓,將mod_jk2放到apache的安裝文件夾下的modules文件夾中。 步驟2:apache配置 在/conf中加入一個work.properties文件,其內容如下: <!--這個文件的作用不是很清楚,總之路徑設置正確就行了。我的apache裝在D:/Apache2,根據情況自己修改。--> [shm] file=D:/ /Apache2/logs/shm.file size=1048576 <!--這個socket channel是必須的,port和host對應於tomcat端的設置。--> #The socket channel [channel.socket:localhost:8009] port=8009 host=localhost <!--worker,必須的。--> #define the worker [ajp13:localhost:8009] channel=channel.socket:localhost:8009 <!--url mapping,我的主要是.jsp和struts的.do,servlet的話設置成[uri:/xxx/*]之類的。--> #uri mapping [uri:/*] #和第一種方式一樣吧^_^ [uri:/*.jsp] [uri:/*.do] worker=ajp13:localhost:8009 在httpd.conf中,在LoadModule那裡加入這句: LoadModule jk2_module modules/mod_jk2.so 在最後加入這句: JkSet config.file "conf/work.properties" 這是告訴apache去哪裡找jk的配置的,根據具體情況修改。 還要修改一下DirectoryIndex,DirectoryIndex index.html index.html.var index.jsp查找「listen」用於本機測試時:Listen 127.0.0.1:80,我的是這樣設置的Listen *:80。 當然還有我們的虛擬目錄: <VirtualHost *:80> ServerAdmin darkxie@hotmail.com DocumentRoot F:/uutang/uutang ServerName dark ErrorLog logs/ErrorLog.txt CustomLog logs/CustomLog.txt common #JkMount /servlet/* ajp13 #JkMount /*.jsp ajp13 #JkMount /*.do ajp13 </VirtualHost>
步驟3:tomcat配置 Tomcat的端口設置為8080。 在/conf文件夾加入jk2.properties文件,其內容如下: # Set the desired handler list handler.list=apr,request,channelSocket # # Override the default port for the socketChannel channelSocket.port=8009 TOMCAT自己已經生成了這個文件,找到相關的地方把注視去掉改一下就成。
注意:用這種方式整合最好是自己編譯mod_jk2.so文件,特別是在unix/linux下,我沒有環境,製作mod_webapp.so沒有自己作過。具體方法,自己去找吧。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| webasp.net |