
2009年8月28日
1.使用DNS輪詢.
2.使用Apache R-proxy方式。
3.使用Apache mod_jk方式.
DNS輪詢的缺點(diǎn)是,當(dāng)集群中某臺服務(wù)器停止之后,用戶由于dns緩存的緣故,便無法訪問服務(wù),
必須等到dns解析更新,或者這臺服務(wù)器重新啟動。
還有就是必須把集群中的所有服務(wù)端口暴露給外界,沒有用apache做前置代理的方式安全,
并且占用大量公網(wǎng)IP地址,而且tomcat還要負(fù)責(zé)處理靜態(tài)網(wǎng)頁資源,影響效率。
優(yōu)點(diǎn)是集群配置最簡單,dns設(shè)置也非常簡單。
R-proxy的缺點(diǎn)是,當(dāng)其中一臺tomcat停止運(yùn)行的時候,apache仍然會轉(zhuǎn)發(fā)請求過去,導(dǎo)致502網(wǎng)關(guān)錯誤。
但是只要服務(wù)器再啟動就不存在這個問題。
mod_jk方式的優(yōu)點(diǎn)是,Apache 會自動檢測到停止掉的tomcat,然后不再發(fā)請求過去。
缺點(diǎn)就是,當(dāng)停止掉的tomcat服務(wù)器再次啟動的時候,Apache檢測不到,仍然不會轉(zhuǎn)發(fā)請求過去。
R-proxy和mod_jk的共同優(yōu)點(diǎn)是.可以只將Apache置于公網(wǎng),節(jié)省公網(wǎng)IP地址資源。
可以通過設(shè)置來實現(xiàn)Apache專門負(fù)責(zé)處理靜態(tài)網(wǎng)頁,讓Tomcat專門負(fù)責(zé)處理jsp和servlet等動態(tài)請求。
共同缺點(diǎn)是:如果前置Apache代理服務(wù)器停止運(yùn)行,所有集群服務(wù)將無法對外提供。
R-proxy和mod_jk對靜態(tài)頁面請求的處理,都可以通設(shè)置來選取一個盡可能優(yōu)化的效果。
這三種方式對實現(xiàn)最佳負(fù)載均衡都有一定不足,mod_jk相對好些,可以通過設(shè)置lbfactor參數(shù)來分配請求任務(wù),但又因為mod_jk2方式不被推薦,mod_jk2已經(jīng)不再被更新了。郁悶中……
哈哈,發(fā)現(xiàn)apache2.2以后與tomcat做負(fù)載均衡不需要用mod_jk2,在配置文件中稍做修改就OK
posted @
2009-08-28 15:08 kit_lo 閱讀(2737) |
評論 (5) |
編輯 收藏
Java EE應(yīng)用的性能問題對嚴(yán)肅的項目和產(chǎn)品來說是一個非常重要的問題。特別是企業(yè)級的應(yīng)用,并發(fā)用戶多,數(shù)據(jù)傳輸量大,業(yè)務(wù)邏輯復(fù)雜,占用系統(tǒng)資源多,因此性能問題在企業(yè)級應(yīng)用變得至關(guān)重要,它和系統(tǒng)的穩(wěn)定性有著直接的聯(lián)系。更加重要的是,性能好的應(yīng)用在完成相同任務(wù)的條件下,能夠占用更少的資源,獲得更好的用戶體驗,換句話說,就是能夠節(jié)省費(fèi)用和消耗,獲得更高的利潤。
要獲得更好的性能,就需要對原來的系統(tǒng)進(jìn)行性能調(diào)優(yōu)。對運(yùn)行在Glassfish上的JavaEE應(yīng)用,調(diào)優(yōu)是一件相對復(fù)雜的事情。在調(diào)優(yōu)以前必須要認(rèn)識到:對JavaEE的系統(tǒng),調(diào)優(yōu)是多層次的。一個JavaEE的應(yīng)用其實是整個系統(tǒng)中很少的一部分。開發(fā)人員所開發(fā)的JavaEE程序,無論是JSP還是 EJB,都是運(yùn)行在JavaEE應(yīng)用服務(wù)器(Glassfish)之上。而應(yīng)用服務(wù)器本身也是Java語言編寫的,需要運(yùn)行在Java虛擬機(jī)之上。 Java虛擬機(jī)也只不過是操作系統(tǒng)的一個應(yīng)用而已,和其他的應(yīng)用(如Apache)對于操作系統(tǒng)來說沒有本質(zhì)的區(qū)別。而操作系統(tǒng)卻運(yùn)行在一定的硬件環(huán)境中,包括CPU,內(nèi)存,網(wǎng)卡和硬盤等等。在這么多的層次中,每一個層次的因素都會影響整個系統(tǒng)的性能。因此,對一個系統(tǒng)的調(diào)優(yōu),事實上需要同時對每個層次都要調(diào)優(yōu)。JavaEE應(yīng)用性能調(diào)優(yōu)不僅僅和Glassfish有關(guān),Java語言有關(guān),還要和操作系統(tǒng)以及硬件都有關(guān)系,需要調(diào)優(yōu)者有綜合的知識和技能。這些不同層面的方法需要綜合縱效,結(jié)合在一起靈活使用,才能快速有效的定位性能瓶頸。下面是一些具體的案例分析:
內(nèi)存泄漏問題
某個JavaEE應(yīng)用運(yùn)行在8顆CPU的服務(wù)器上。上線運(yùn)行發(fā)現(xiàn)性能不穩(wěn)定。性能隨著時間的增加而越來越慢。通過操作系統(tǒng)的工具(mpstat),發(fā)現(xiàn)在系統(tǒng)很慢的時候,只有一顆CPU很忙,其他的CPU都很空閑。因此懷疑是Java虛擬機(jī)經(jīng)常進(jìn)行內(nèi)存回收,因為虛擬機(jī)在內(nèi)存回收的時候,有的回收算法通常只能運(yùn)行在一個CPU上。通過Java虛擬機(jī)的工具“jstat”可以清楚的看到,Java虛擬機(jī)進(jìn)行內(nèi)存回收的頻率非常高,幾乎每5秒中就有一次,每次回收的時間為2秒鐘。另外,通過“jstat”的輸出還發(fā)現(xiàn)每次回收釋放的內(nèi)存非常有限,大多數(shù)對象都無法回收。這種現(xiàn)象很大程度上暗示著內(nèi)存泄漏。使用 Java虛擬機(jī)的工具“jmap”來獲得當(dāng)前的一個內(nèi)存映象。發(fā)現(xiàn)有很多(超過10000)個的session對象。這是不正常的一個現(xiàn)象。一般來說, session對應(yīng)于一個用戶的多次訪問,當(dāng)用戶退出的時候,session就應(yīng)該失效,對象應(yīng)該被回收。當(dāng)我們和這個系統(tǒng)的開發(fā)工程師了解有關(guān) session的設(shè)置,發(fā)現(xiàn)當(dāng)他們部署應(yīng)用的時候,竟然將session的timeout時間設(shè)置為50分鐘,并且沒有提供logout的接口。這樣的設(shè)置下,每個session的數(shù)據(jù)都會保存50分鐘才會被回收。根據(jù)我們的建議,系統(tǒng)提供了logout的鏈接,并且告訴用戶如果退出應(yīng)用,應(yīng)該點(diǎn)擊這個 logout的鏈接;并且將session的timeout時間修改為5分鐘。通過幾天的測試,證明泄漏的問題得到解決。
數(shù)據(jù)庫連接池問題
某財務(wù)應(yīng)用運(yùn)行在JavaEE服務(wù)器上,后臺連接Oracle數(shù)據(jù)庫。并發(fā)用戶數(shù)量超過100人左右的時候系統(tǒng)停止響應(yīng)。通過操作系統(tǒng)層面的進(jìn)程監(jiān)控工具發(fā)現(xiàn)進(jìn)程并沒有被殺死或掛起,而CPU使用率幾乎為零。那么是什么原因?qū)е孪到y(tǒng)停止響應(yīng)用戶請求呢?我們利用Java虛擬機(jī)的工具(kill -3 pid)將當(dāng)前的所有線程狀態(tài)DUMP出來,發(fā)現(xiàn)JavaEE服務(wù)器的大部分處理線程都在等待數(shù)據(jù)庫連接池的連接,而那些已經(jīng)獲得數(shù)據(jù)庫連接的線程卻處于阻塞狀態(tài)。數(shù)據(jù)庫管理員應(yīng)要求檢查了數(shù)據(jù)庫的狀態(tài),發(fā)現(xiàn)所有的連接的session都處于死鎖狀態(tài)。顯然,這是因為數(shù)據(jù)庫端出現(xiàn)了死鎖的操作,阻塞了那些有數(shù)據(jù)庫操作的請求,占用了所有數(shù)據(jù)庫連接池中的連接。后續(xù)的請求如果還要從連接池中獲取連接,就會阻塞在連接池上。當(dāng)解決數(shù)據(jù)庫死鎖的問題之后,性能問題迎刃而解。
大對象緩存問題
電信應(yīng)用運(yùn)行在64位Java虛擬機(jī)上,系統(tǒng)運(yùn)行得很不穩(wěn)定,系統(tǒng)經(jīng)常停止響應(yīng)。使用進(jìn)程工具查看,發(fā)現(xiàn)進(jìn)程并沒有被殺死或掛起。利用Java虛擬機(jī)的工具發(fā)現(xiàn)系統(tǒng)在長時間的進(jìn)行內(nèi)存回收,內(nèi)存回收的時間長達(dá)15分鐘,整個系統(tǒng)在內(nèi)存回收的時候就像掛起一樣。另外還觀察到系統(tǒng)使用了12G的內(nèi)存(因為是 64位虛擬機(jī)所以突破了4G內(nèi)存的限制)。從開發(fā)人員那里了解到,這個應(yīng)用為了提高性能,大量使用了對象緩存,但是事與愿違,在Java中使用過多的內(nèi)存,雖然在正常運(yùn)行的時候能夠獲得很好的性能,但是會大大增加內(nèi)存回收的時間。特別是對象緩存,本系統(tǒng)使用了8G的緩存空間,共緩存了6000多萬個對象,對這些對象的遍歷導(dǎo)致了長時間的內(nèi)存回收。根據(jù)我們的建議,將緩存空間減少到1G,并調(diào)整回收算法(使用增量回收的算法),使得系統(tǒng)由于內(nèi)存回收而造成的最大停頓時間減少到4秒,基本滿足用戶的需求。
外部命令問題
數(shù)字校園應(yīng)用運(yùn)行在4CPU的Solaris10服務(wù)器上,中間件為JavaEE服務(wù)器。系統(tǒng)在做大并發(fā)壓力測試的時候,請求響應(yīng)時間比較慢,通過操作系統(tǒng)的工具(mpstat)發(fā)現(xiàn)CPU使用率比較高。并且系統(tǒng)占用絕大多數(shù)的CPU資源而不是應(yīng)用本身。這是個不正常的現(xiàn)象,通常情況下用戶應(yīng)用的CPU占用率應(yīng)該占主要地位,才能說明系統(tǒng)是正常工作。通過Solaris 10的Dtrace腳本,我們查看當(dāng)前情況下哪些系統(tǒng)調(diào)用花費(fèi)了最多的CPU資源,竟然發(fā)現(xiàn)最花費(fèi)CPU的系統(tǒng)調(diào)用是“fork”。眾所周知, “fork”系統(tǒng)調(diào)用是用來產(chǎn)生新的進(jìn)程,在Java虛擬機(jī)中只有線程的概念,絕不會有進(jìn)程的產(chǎn)生。這是個非常異常的現(xiàn)象。通過本系統(tǒng)的開發(fā)人員,我們找到了答案:每個用戶請求的處理都包含執(zhí)行一個外部shell腳本,來獲得系統(tǒng)的一些信息。這是通過Java的“Runtime.getRuntime ().exec”來完成的,但是這種方法在Java中非常消耗資源。Java虛擬機(jī)執(zhí)行這個命令的方式是:首先克隆一個和當(dāng)前虛擬機(jī)一樣的進(jìn)程,再用這個新的進(jìn)程去執(zhí)行外部命令,最后再退出這個進(jìn)程。如果頻繁執(zhí)行這個操作,系統(tǒng)的消耗會很大,不僅在CPU,內(nèi)存操作也很重。用戶根據(jù)建議去掉這個shell 腳本執(zhí)行的語句,系統(tǒng)立刻回復(fù)了正常。
文件操作問題
內(nèi)容管理(CMS)系統(tǒng)運(yùn)行在JavaEE服務(wù)器上,當(dāng)系統(tǒng)長時間運(yùn)行以后,性能非常差,用戶請求的延時比系統(tǒng)剛上線的時候要大很多,并且用戶的并發(fā)量很小,甚至是單個用戶也很慢。通過操作系統(tǒng)的工具觀察,一切都很正常,CPU利用率不高,IO也不是很大,內(nèi)存很富余,網(wǎng)絡(luò)幾乎沒有壓力(因為并發(fā)用戶少)。先不考慮線程互鎖的問題,因為單個用戶性能也不好。通過Java虛擬機(jī)觀察也沒有發(fā)現(xiàn)什么問題(內(nèi)存回收很少發(fā)生)。這使得我們不得不使用代碼跟蹤器來全程跟蹤代碼。我們采用了Netbeans的Profiler,跟蹤的結(jié)果非常意外,用戶請求的90%的時間在創(chuàng)建新文件。從系統(tǒng)設(shè)計人員了解到,此系統(tǒng)使用了一個目錄用于保存所有上傳和共享的文件,文件用其命名方式來唯一區(qū)別于其他文件。我們查看了那個文件目錄,發(fā)現(xiàn)該目錄下已經(jīng)擁有80萬個文件了。這時候我們才定位到問題了:在同個目錄下放置太多的文件,在創(chuàng)建新文件的時候,系統(tǒng)的開銷是比較大的,例如為了防止重名,文件系統(tǒng)會遍歷當(dāng)前目錄下所有的文件名等等。根據(jù)我們的建議,將文件分類保存在不同的目錄下,性能有了大幅度的提高。
高速緩存命中率問題
運(yùn)行在JavaEE服務(wù)器上的ERP系統(tǒng),在CPU充分利用的情況下性能仍然不太好。從操作系統(tǒng)層面上觀察不到什么大問題,而且ERP系統(tǒng)過于復(fù)雜,代碼跟蹤比較困難。于是進(jìn)行了CPU狀態(tài)的進(jìn)一步檢查,發(fā)現(xiàn)CPU的TLB命中率不是很高,于是對Java虛擬機(jī)的啟動參數(shù)進(jìn)行了修改,強(qiáng)迫虛擬機(jī)使用大尺寸的內(nèi)存頁面,提高TLB的命中率。下面的參數(shù)是在Sun的HOTSPOT中調(diào)整大尺寸(4M)頁面的設(shè)置:
-XX:+AggressiveHeap
-XX:LargePageSizeInBytes=256m
通過調(diào)整,TLB命中明顯提高,性能也得到近40%的提升。
轉(zhuǎn)載之:http://developers.sun.com.cn/blog/yutoujava/entry/8
posted @
2009-08-28 15:07 kit_lo 閱讀(1977) |
評論 (1) |
編輯 收藏
負(fù)載均衡器通常稱為四層交換機(jī)或七層交換機(jī)。四層交換機(jī)主要分析IP層及TCP/UDP層,實現(xiàn)四層流量負(fù)載均衡。七層交換機(jī)除了支持四層負(fù)載均衡以外,還有分析應(yīng)用層的信息,如HTTP協(xié)議URI或Cookie信息。
一、F5配置步驟:
1、F5組網(wǎng)規(guī)劃
(1)組網(wǎng)拓樸圖(具體到網(wǎng)絡(luò)設(shè)備物理端口的分配和連接,服務(wù)器網(wǎng)卡的分配與連接)
(2)IP地址的分配(具體到網(wǎng)絡(luò)設(shè)備和服務(wù)器網(wǎng)卡的IP地址的分配)
(3)F5上業(yè)務(wù)的VIP、成員池、節(jié)點(diǎn)、負(fù)載均衡算法、策略保持方法的確定
2、F5配置前的準(zhǔn)備工作
(1)版本檢查
f5-portal-1:~# b version
Kernel:
BIG-IP Kernel 4.5PTF-07 Build18
(2)時間檢查--如不正確,請到單用戶模式下進(jìn)行修改
f5-portal-1:~# date
Thu May 20 15:05:10 CST 2004
(3)申請license--現(xiàn)場用的F5都需要自己到F5網(wǎng)站上申請license
3、F5 的通用配置
(1)在安全要求允許的情況下,在setup菜單中可以打開telnet及ftp功能,便于以后方便維護(hù)
(2)配置vlan unique_mac選項,此選項是保證F5上不同的vlan 的MAC地址不一樣。在缺省情況下,F(xiàn)5的各個vlan的MAC地址是一樣的,建議在配置時,把此項統(tǒng)一選擇上。可用命令ifconfig –a來較驗
具體是system/Advanced Properties/vlan unique_mac
(3)配置snat any_ip選項選項,此選項為了保證內(nèi)網(wǎng)的機(jī)器做了snat后,可以對ping的數(shù)據(jù)流作轉(zhuǎn)換。Ping是第三層的數(shù)據(jù)包,缺省情況下F5是不對ping的數(shù)據(jù)包作轉(zhuǎn)換,也就是internal vlan的主機(jī)無法ping external vlan的機(jī)器。(注意:還可以采用telnet來驗證。)
具體是system/Advanced Properties/snat any_ip
4、F5 的初始化配置
建議在對F5進(jìn)行初始時都用命令行方式來進(jìn)行初始化(用Web頁面初始化的方式有時會有問題)。登錄到命令行上,運(yùn)行config或setup命令可以進(jìn)行初始化配置。初次運(yùn)行時會提示一些license的信息。
default:~# config
5、F5雙機(jī)切換監(jiān)控配置(有F5雙機(jī)時需要)
(1)在web頁面中選擇相應(yīng)的vlan,在arm failsafe選擇則可。Timeout為從F5收不到包的時間起,經(jīng)過多長時間就發(fā)生切換。此配置不能同步,需要在F5的主備機(jī)上同時配置。每個vlan都可以配置vlan arm failsafe。
具體在Network下
(2)在web頁面中選擇system,在redundant properties中把gateway failsafe選擇則可。Router是需要監(jiān)控的地址。此配置不能同步,需要在F5的主備機(jī)上同時配置。一套F5上只能配置一個gateway failsafe
具體在system/redundant properties/gateway failsafe
6、F5 MAC masquerade配置
Mac Masquerading是F5的Shared IP Address (Floating)的MAC地址,F(xiàn)5如果不配置此項,則shared IP Address的MAC地址與每臺F5的vlan self IP Address的MAC地址是一樣的。
一般服務(wù)器是以shared IP Address為網(wǎng)關(guān),在兩臺F5上都配置了Mac Masquerade(相同的MAC地址),這樣當(dāng)F5發(fā)生切換后,服務(wù)器上shared IP address的MAC不變,保證了業(yè)務(wù)的不中斷
具體在Network下
7、F5的pool配置
(1)在配置工具Web頁面的導(dǎo)航面板中選擇“Pools”中的“Pools”標(biāo)簽,點(diǎn)擊“ADD”按鈕添加服務(wù)器池(Pool)。
(2)在池屬性(Pool Properties)中的“Load Balancing Method”表格中選擇負(fù)載均衡策略,通常采用默認(rèn)策略:“Round Robin”
(3)在“Resouces”表格中的“Member Address”文本框輸入成員IP地址,在“Service”文本框中輸入服務(wù)端口,點(diǎn)擊“>>”添加到“Current Members”當(dāng)前成員列表中。
(4)添加所有組成員,點(diǎn)擊“Done”完成配置。
(5)在“Pools”中的“Pool Name”列選中特定池,然后池屬性頁面中選擇“Persistence”標(biāo)簽。
(6)在“Persistence Type”表格中選定會話保持類型。點(diǎn)擊“Apply”應(yīng)用配置。
8、F5的virtual server配置
(1)在配置工具Web頁面的導(dǎo)航面板中選擇“Virtual Servers”中的“Virtual Servers”標(biāo)簽,點(diǎn)擊“ADD”按鈕添加虛擬服務(wù)器。
(2)在“Add Virtual Server”窗口的“Address”文本框中輸入虛擬服務(wù)器IP地址,并在“Service”文本框中輸入服務(wù)端口號或在下拉框中選擇現(xiàn)有的服務(wù)名稱,點(diǎn)擊“Next”執(zhí)行下一步。
(3)在“Add Virtual Server”窗口的“Configure Basic Properties”頁面中點(diǎn)擊“Next”執(zhí)行下一步。 在“Add Virtual Server”窗口的“Select Physical Resources”頁面中點(diǎn)擊單選按鈕“Pool”,并在下拉框中選擇虛擬服務(wù)器對應(yīng)的負(fù)載均衡池。
(4)按“Done”完成創(chuàng)建虛擬服務(wù)器。
9、F5的monitor的配置
(1)在配置工具Web頁面的導(dǎo)航面板中選擇“Monitor”中的“Monitors”標(biāo)簽,點(diǎn)擊“ADD”按鈕添加監(jiān)控
(2)根據(jù)需要選擇相關(guān)關(guān)聯(lián)類型:“Node Associations”標(biāo)簽、Node Address Associations”標(biāo)簽、Service Associations”標(biāo)簽。
(3)被選關(guān)聯(lián)標(biāo)簽中,在“Choose Monitor”表格中選擇監(jiān)控名稱,點(diǎn)擊“>>”按鈕添加到“Monitor Rule”監(jiān)控規(guī)格文本框中。監(jiān)控規(guī)則可以為一條或多條。
(4)選擇監(jiān)控規(guī)則后,在對應(yīng)節(jié)點(diǎn)的“Associate Current Monitor Rule”復(fù)選框中選中。如果欲刪除監(jiān)控關(guān)聯(lián),則選中對應(yīng)節(jié)點(diǎn)的“Delete Existing Assocation”復(fù)選框。
(5)點(diǎn)擊“Apply”關(guān)聯(lián)監(jiān)控
10、F5的SNAT配置
(1)在配置工具Web頁面的導(dǎo)航面板中選擇“NATs”中的“SNATs”標(biāo)簽,點(diǎn)擊“ADD”按鈕添加SNAT地址。
(2)在“Add SNAT”窗口中“Translation Address”的“IP”文本框中輸入SNAT IP地址,并在“Origin List”的“Origin Address”文本框中輸入節(jié)點(diǎn)IP地址或在“Origin VLAN”下拉框中選擇VLAN名稱,點(diǎn)擊“>>”加入“Current List”列表。
(3)按“Done”完成添加SNAT IP地址。
11、F5主備機(jī)同步及切換校驗
具體在system/Redundant Properties/synchonize Config...
12、業(yè)務(wù)的校驗
F5主備機(jī)切換的校驗
F5主備機(jī)業(yè)務(wù)運(yùn)行的校驗
其中1~6是基本配置,7~10業(yè)務(wù)配置,11~12校驗
二、F5負(fù)載均衡器的維護(hù)
1、F5節(jié)點(diǎn)及應(yīng)用的檢查
通過“System -> Network Map”頁面查看節(jié)點(diǎn)及應(yīng)用狀態(tài)
綠色:節(jié)點(diǎn)或虛擬服務(wù)器為“UP”
紅色:節(jié)點(diǎn)或虛擬服務(wù)器狀態(tài)為“Down”
灰色:節(jié)點(diǎn)或虛擬服務(wù)器被禁用
2、日志的檢查
(1)當(dāng)天日志:從web上查看logs中的system log、bigip log、monitor log,看日志中是否有異常。
(2)7天內(nèi)的日志
系統(tǒng)日志文件 - /var/log/messages消息, 系統(tǒng)消息
BIG-IP 日志文件 - /var/log/bigip
“External” BIG-IP events
Monitor 日志文件 - /var/log/bigd
“Internal” BIG-IP Events
3DNS 日志文件 - /var/log/3dns
3DNS Information
用gzcat、more、vi命令打開
3、F5流量的檢查
(1)業(yè)務(wù)上的基本維護(hù)主要是在F5上查看F5分發(fā)到各節(jié)點(diǎn)的connect是否負(fù)載均衡,一般不應(yīng)有數(shù)量級的差別
(2)通過WEB->pool-> pool statistics中查看connection項中的total和current項,不應(yīng)有明顯的數(shù)量級的差別
(3)F5 qkview命令
執(zhí)行qkview,執(zhí)行完成后將輸出信息保存在文件“/var/tmp/-tech.out”中,供高級技術(shù)支持用
(4)F5 tcpdump命令
TCPDUMP是Unix系統(tǒng)常用的報文分析工具,TCPDUMP經(jīng)常用于故障定位,如會話保持失效、SNAT通信問題等
tcpdump [ -adeflnNOpqRStvxX ] [ -c count ] [ -F file ]
[ -i interface ] [ -m module ] [ -r file ]
[ -s snaplen ] [ -T type ] [ -w file ]
[ -E algo:secret ] [ expression ]
posted @
2009-08-28 15:06 kit_lo 閱讀(3626) |
評論 (0) |
編輯 收藏
1. Tomcat是Apache鼎力支持的Java Web應(yīng)用服務(wù)器,由于它優(yōu)秀的穩(wěn)定性以及豐富的文檔資料,廣泛的使用人群,從而在開源領(lǐng)域受到最廣泛的青睞。
2. Jboss作為Java EE應(yīng)用服務(wù)器,它不但是Servlet容器,而且是EJB容器,從而受到企業(yè)級開發(fā)人員的歡迎,從而彌補(bǔ)了Tomcat只是一個Servlet容器的缺憾。
3. Resin也僅僅是一個Servlet容器,然而由于它優(yōu)秀的運(yùn)行速度,使得它在輕量級Java Web領(lǐng)域備受喜愛,特別是在互聯(lián)網(wǎng)Web服務(wù)領(lǐng)域,眾多知名公司都采用其作為他們的Java Web應(yīng)用服務(wù)器,譬如163、ku6等。
在商用應(yīng)用服務(wù)器里主要有:Weblogic、Websphere,其中Weblogic我也使用過很長一段時間,當(dāng)時也只用其當(dāng)Servlet容器,然而就在同等條件下,在性能及易用性等方面,要比Tomcat優(yōu)秀很多。
4.glassfish是Sun公司推出的Java EE服務(wù)器,一個比較活躍的開源社區(qū),不斷的通過社區(qū)的反饋來提高其的可用性,經(jīng)過glassfish v1 glassfish v2 到今天的glassfish v3 ,它已經(jīng)走向成熟.Glassfish是一個免費(fèi)、開放源代碼的應(yīng)用服務(wù),它實現(xiàn)了Java EE 5,Java EE 5 平臺包括了以下最新技術(shù):EJB 3.0、JSF 1.2、Servlet 2.5、JSP 2.1、JAX-WS 2.0、JAXB 2.0、 Java Persistence 1.0、Common Annonations 1.0、StAX 1.0等.
支持集群,通過內(nèi)存中會話狀態(tài)復(fù)制,增強(qiáng)了部署體系結(jié)構(gòu)的可用性與可伸縮性,它對集群有著很好的支持,可以簡單到通過添加機(jī)器,就可輕松的提高網(wǎng)站的帶負(fù)載能力,在解析能力方面,它對html的吞吐能力與apache服務(wù)器不分上下,就是tomcat所不能比的,支持目錄部署,熱部署,解決了tomcat對熱部署能力的缺陷.在版本方面做的更加人性化,有開發(fā)時用的簡化版,專門用于部署web項目的版本,還要完全符合j2ee標(biāo)準(zhǔn)的版本.
posted @
2009-08-28 15:05 kit_lo 閱讀(5560) |
評論 (0) |
編輯 收藏
為應(yīng)用程序添加搜索能力經(jīng)常是一個常見的需求。本文介紹了一個框架,開發(fā)者可以使用它以最小的付出實現(xiàn)搜索引擎功能,理想情況下只需要一個配置文件。該框架基于若干開源的庫和工具,如 Apache Lucene,Spring 框架,cpdetector 等。它支持多種資源。其中兩個典型的例子是數(shù)據(jù)庫資源和文件系統(tǒng)資源。Indexer 對配置的資源進(jìn)行索引并傳輸?shù)街醒敕?wù)器,之后這些索引可以通過 API 進(jìn)行搜索。Spring 風(fēng)格的配置文件允許清晰靈活的自定義和調(diào)整。核心 API 也提供了可擴(kuò)展的接口。
引言
為應(yīng)用程序添加搜索能力經(jīng)常是一個常見的需求。盡管已經(jīng)有若干程序庫提供了對搜索基礎(chǔ)設(shè)施的支持,然而對于很多人而言,使用它們從頭開始建立一個搜索引擎將是一個付出不小而且可能乏味的過程。另一方面,很多的小型應(yīng)用對于搜索功能的需求和應(yīng)用場景具有很大的相似性。本文試圖以對多數(shù)小型應(yīng)用的適用性為出發(fā)點(diǎn),用 Java 語言構(gòu)建一個靈活的搜索引擎框架。使用這個框架,多數(shù)情形下可以以最小的付出建立起一個搜索引擎。最理想的情況下,甚至只需要一個配置文件。特殊的情形下,可以通過靈活地對框架進(jìn)行擴(kuò)展?jié)M足需求。當(dāng)然,如題所述,這都是借助開源工具的力量。
基礎(chǔ)知識
Apache Lucene 是開發(fā)搜索類應(yīng)用程序時最常用的 Java 類庫,我們的框架也將基于它。為了下文更好的描述,我們需要先了解一些有關(guān) Lucene 和搜索的基礎(chǔ)知識。注意,本文不關(guān)注索引的文件格式、分詞技術(shù)等話題。
什么是搜索和索引
從用戶的角度來看,搜索的過程是通過關(guān)鍵字在某種資源中尋找特定的內(nèi)容的過程。而從計算機(jī)的角度來看,實現(xiàn)這個過程可以有兩種辦法。一是對所有資源逐個與關(guān)鍵字匹配,返回所有滿足匹配的內(nèi)容;二是如同字典一樣事先建立一個對應(yīng)表,把關(guān)鍵字與資源的內(nèi)容對應(yīng)起來,搜索時直接查找這個表即可。顯而易見,第二個辦法效率要高得多。建立這個對應(yīng)表事實上就是建立逆向索引(inverted index)的過程。
Lucene 基本概念
Lucene 是 Doug Cutting 用 Java 開發(fā)的用于全文搜索的工具庫。在這里,我假設(shè)讀者對其已有基本的了解,我們只對一些重要的概念簡要介紹。要深入了解可以參考 參考資源 中列出的相關(guān)文章和圖書。下面這些是 Lucene 里比較重要的類。
Document:索引包含多個 Document。而每個 Document 則包含多個 Field 對象。Document 可以是從數(shù)據(jù)庫表里取出的一堆數(shù)據(jù),可以是一個文件,也可以是一個網(wǎng)頁等。注意,它不等同于文件系統(tǒng)中的文件。
Field:一個 Field 有一個名稱,它對應(yīng) Document的一部分?jǐn)?shù)據(jù),表示文檔的內(nèi)容或者文檔的元數(shù)據(jù)(與下文中提到的資源元數(shù)據(jù)不是一個概念)。一個 Field 對象有兩個重要屬性:Store ( 可以有 YES, NO, COMPACT 三種取值 ) 和 Index ( 可以有 TOKENIZED, UN_TOKENIZED, NO, NO_NORMS 四種取值 )
Query:抽象了搜索時使用的語句。
IndexSearcher:提供Query對象給它,它利用已有的索引進(jìn)行搜索并返回搜索結(jié)果。
Hits:一個容器,包含了指向一部分搜索結(jié)果的指針。
使用 Lucene 來進(jìn)行編制索引的過程大致為:將輸入的數(shù)據(jù)源統(tǒng)一為字符串或者文本流的形式,然后從數(shù)據(jù)源提取數(shù)據(jù),創(chuàng)建合適的 Field 添加到對應(yīng)該數(shù)據(jù)源的 Document 對象之中。
系統(tǒng)概覽
要建立一個通用的框架,必須對不同情況的共性進(jìn)行抽象。反映到設(shè)計需要注意兩點(diǎn)。一是要提供擴(kuò)展接口;二是要盡量降低模塊之間的耦合程度。我們的框架很簡單地分為兩個模塊:索引模塊和搜索模塊。索引模塊在不同的機(jī)器上各自進(jìn)行對資源的索引,并把索引文件(事實上,下面我們會說到,還有元數(shù)據(jù))統(tǒng)一傳輸?shù)酵粋€地方(可以是在遠(yuǎn)程服務(wù)器上,也可以是在本地)。搜索模塊則利用這些從多個索引模塊收集到的數(shù)據(jù)完成用戶的搜索請求。
圖 1 展現(xiàn)了整體的框架。可以看到,兩個模塊之間相對是獨(dú)立的,它們之間的關(guān)聯(lián)不是通過代碼,而是通過索引和元數(shù)據(jù)。在下文中,我們將會詳細(xì)介紹如何基于開源工具設(shè)計和實現(xiàn)這兩個模塊。
圖 1. 系統(tǒng)架構(gòu)圖
建立索引
可以進(jìn)行索引的對象有很多,如文件、網(wǎng)頁、RSS Feed 等。在我們的框架中,我們定義可以進(jìn)行索引的一類對象為資源。從實現(xiàn)細(xì)節(jié)上來說,從一個資源中可以提取出多個 Document 對象。文件系統(tǒng)資源和數(shù)據(jù)庫結(jié)果集資源都是資源的代表性例子。
前面提到,從資源中收集到的索引被統(tǒng)一傳送到同一個地方,以被搜索模塊所用。顯然除了索引之外,搜索模塊需要有對資源更多的了解,如資源的名稱、搜索該資源后搜索結(jié)果的呈現(xiàn)格式等。這些額外的附加信息稱為資源的元數(shù)據(jù)。元數(shù)據(jù)和索引數(shù)據(jù)一同被收集起來,放置到某個特定的位置。
簡要地介紹過資源的概念之后,我們首先為其定義一個 Resource 接口。這個接口的聲明如下。
清單 1. Resource 接口
public interface Resource {
// RequestProcessor 對象被動地從資源中提取 Document,并返回提取的數(shù)量
public int extractDocuments(ResourceProcessor processor);
// 添加的 DocumentListener 將在每一個 Document 對象被提取出時被調(diào)用
public void addDocumentListener(DocumentListener l);
// 返回資源的元數(shù)據(jù)
public ResourceMetaData getMetaData();
}
其中元數(shù)據(jù)包含的字段見下表。在下文中,我們還會對元數(shù)據(jù)的用途做更多的介紹。
表 1. 資源元數(shù)據(jù)包含的字段
屬性 類型 含義
resourceName String 資源的唯一名稱
resourceDescription String 資源的介紹性文字
hitTextPattern String 當(dāng)文檔被搜索到時,這個 pattern 規(guī)定了結(jié)果顯示的格式
searchableFields String[] 可以被搜索的字段名稱
而 DocumentListener 的代碼如下。
清單 2. DocumentListener 接口
public interface DocumentListener extends EventListener {
public void documentExtracted(Document doc);
}
為了讓索引模塊能夠知道所有需要被索引的資源,我們在這里使用 Spring 風(fēng)格的 XML 文件配置索引模塊中的所有組件,尤其是所有資源。您可以在 下載部分 查看一個示例配置文件。
為什么選擇使用 Spring 風(fēng)格的配置文件?
這主要有兩個好處:
僅依賴于 Spring Core 和 Spring Beans 便免去了定義配置機(jī)制和解析配置文件的負(fù)擔(dān);
Spring 的 IoC 機(jī)制降低了框架的耦合性,并使擴(kuò)展框架變得簡單;
基于以上內(nèi)容,我們可以大致描述出索引模塊工作的過程:
首先在 XML 配置的 bean 中找出所有 Resource 對象;
對每一個調(diào)用其 extractDocuments() 方法,這一步除了完成對資源的索引外,還會在每次提取出一個 Document 對象之后,通知注冊在該資源上的所有 DocumentListener;
接著處理資源的元數(shù)據(jù)(getMetaData() 的返回值);
將緩存里的數(shù)據(jù)寫入到本地磁盤或者傳送給遠(yuǎn)程服務(wù)器;
在這個過程中,有兩個地方值得注意。
第一,對資源可以注冊 DocumentListener 使得我們可以在運(yùn)行時刻對索引過程有更為動態(tài)的控制。舉一個簡單例子,對某個文章發(fā)布站點(diǎn)的文章進(jìn)行索引時,一個很正常的要求便是發(fā)布時間更靠近當(dāng)前時間的文章需要在搜索結(jié)果中排在靠前的位置。每篇文章顯然對應(yīng)一個 Document 對象,在 Lucene 中我們可以通過設(shè)置 Document 的 boost 值來對其進(jìn)行加權(quán)。假設(shè)其中文章發(fā)布時間的 Field 的名稱為 PUB_TIME,那么我們可以為資源注冊一個 DocumentListener,當(dāng)它被通知時,則檢測 PUB_TIME 的值,根據(jù)距離當(dāng)前時間的遠(yuǎn)近進(jìn)行加權(quán)。
第二點(diǎn)很顯然,在這個過程中,extractDocuments() 方法的實現(xiàn)依不同類型的資源而各異。下面我們主要討論兩種類型的資源:文件系統(tǒng)資源和數(shù)據(jù)庫結(jié)果集資源。這兩個類都實現(xiàn)了上面的 接口。
文件系統(tǒng)資源
對文件系統(tǒng)資源的索引通常從一個基目錄開始,遞歸處理每個需要進(jìn)行索引的文件。該資源有一個字符串?dāng)?shù)組類型的 excludedFiles 屬性,表示在處理文件時需要排除的文件絕對路徑的正則表達(dá)式。在遞歸遍歷文件系統(tǒng)樹的同時,絕對路徑匹配 excludedFiles 中任意一項的文件將不會被處理。這主要是考慮到一般我們只需要對一部分文件夾(比如排除可能存在的備份目錄)中的一部分文件(如 doc, ppt 文件等)進(jìn)行索引。
除了所有文件共有的文件名、文件路徑、文件大小和修改時間等 Field,不同類型的文件需要有不同的處理方法。為了保留靈活性,我們使用 Strategy 模式封裝對不同類型文件的處理方式。為此我們抽象出一個 DocumentBuilder 的接口,該接口僅定義了一個方法如下:
清單 3. DocumentBuilder 接口
public interface DocumentBuilder {
Document buildDocument(InputStream is);
}
什么是 Strategy 模式?
根據(jù) Design patterns: Elements of reusable object orientated software 一書:Strategy 模式“定義一系列的算法,把它們分別封裝起來,并且使它們相互可以替換。這個模式使得算法可以獨(dú)立于使用它的客戶而變化。”
不同的 DocumentBuilder(Strategy) 用于從一個輸入流中讀取數(shù)據(jù),處理不同類型的文件。對于常見的文件格式來說,都有合適的開源工具幫助進(jìn)行解析。在下表中我們列舉一些常見文件類型的解析辦法。
文件類型 常用擴(kuò)展名 可以使用的解析辦法
純文本文檔 txt 無需類庫解析
RTF 文檔 rtf 使用 javax.swing.text.rtf.RTFEditorKit 類
Word 文檔(非 OOXML 格式) doc Apache POI (可配合使用 POI Scratchpad)
PowerPoint 演示文稿(非 OOXML 格式) xls Apache POI (可配合使用 POI Scratchpad)
PDF 文檔 pdf PDFBox(可能中文支持欠佳)
HTML 文檔 htm, html JTidy, Cobra
這里以 Word 文件為例,給出一個簡單的參考實現(xiàn)。
清單 4. 解析純文本內(nèi)容的實現(xiàn)
// WordDocument 是 Apache POI Scratchpad 中的一個類
Document buildDocument(InputStream is) {
String bodyText = null;
try {
WordDocument wordDoc = new WordDocument(is);
StringWriter sw = new StringWriter();
wordDoc.writeAllText(sw);
sw.close();
bodyText = sw.toString();
} catch (Exception e) {
throw new DocumentHandlerException("Cannot extract text from a Word document", e);
}
if ((bodyText != null) && (bodyText.trim().length() > 0)) {
Document doc = new Document();
doc.add(new Field("body", bodyText, Field.Store.YES, Field.Index.TOKENIZED));
return doc;
}
return null;
}
那么如何選擇合適的 Strategy 來處理文件呢?UNIX 系統(tǒng)下的 file(1) 工具提供了從 magicnumber 獲取文件類型的功能,我們可以使用 Runtime.exec() 方法調(diào)用這一命令。但這需要在有 file(1) 命令的情況下,而且并不能識別出所有文件類型。在一般的情況下我們可以簡單地根據(jù)擴(kuò)展名來使用合適的類處理文件。擴(kuò)展名和類的映射關(guān)系寫在 properties 文件中。當(dāng)需要添加對新的文件類型的支持時,我們只需添加一個新的實現(xiàn) DocumentBuilder 接口的類,并在映射文件中添加一個映射關(guān)系即可。
數(shù)據(jù)庫結(jié)果集資源
大多數(shù)應(yīng)用使用數(shù)據(jù)庫作為永久存儲,對數(shù)據(jù)庫查詢結(jié)果集索引是一個常見需求。
生成一個數(shù)據(jù)庫結(jié)果集資源的實例需要先提供一個查詢語句,然后執(zhí)行查詢,得到一個結(jié)果集。這個結(jié)果集中的內(nèi)容便是我們需要進(jìn)行索引的對象。extractDocuments 的實現(xiàn)便是為結(jié)果集中的每一行創(chuàng)建一個 Document 對象。和文件系統(tǒng)資源不同的是,數(shù)據(jù)庫資源需要放入 Document 中的 Field 一般都存在在查詢結(jié)果集之中。比如一個簡單的文章發(fā)布站點(diǎn),對其后臺數(shù)據(jù)庫執(zhí)行查詢 SELECT ID, TITLE, CONTENT FROM ARTICLE 返回一個有三列的結(jié)果集。對結(jié)果集的每一行都會被提取出一個 Document 對象,其中包含三個 Field,分別對應(yīng)這三列。
然而不同 Field 的類型是不同的。比如 ID 字段一般對應(yīng) Store.YES 和 Index.NO 的 Field;而 TITLE 字段則一般對應(yīng) Store.YES 和 Index.TOKENIZED 的 Field。為了解決這個問題,我們在數(shù)據(jù)庫結(jié)果集資源的實現(xiàn)中提供一個類型為 Properties 的 fieldTypeMappings 屬性,用于設(shè)置數(shù)據(jù)庫字段所對應(yīng)的 Field 的類型。對于前面的情況來說,這個屬性可能會被配置成類似這樣的形式:
ID = YES, NO
TITLE = YES, TOKENIZED
CONTENT = NO, TOKENIZED
配合這個映射,我們便可以生成合適類型的 Field,完成對結(jié)果集索引的工作。
收集索引
完成對資源的索引之后,還需要讓索引為搜索模塊所用。前面我們已經(jīng)說過這里介紹的框架主要用于小型應(yīng)用,考慮到復(fù)雜性,我們采取簡單地將分布在各個機(jī)器上的索引匯總到一個地方的策略。
匯總索引的傳輸方式可以有很多方案,比如使用 FTP、HTTP、rsync 等。甚至索引模塊和搜索模塊可以位于同一臺機(jī)器上,這種情況下只需要將索引進(jìn)行本地拷貝即可。同前面類似,我們定義一個 Transporter 接口。
清單 5. Transporter 接口
public interface Transporter {
public void transport(File file);
}
以 FTP 方式傳輸為例,我們使用 Commons Net 完成傳輸?shù)牟僮鳌?/span>
public void transport(File file) throws TransportException {
FTPClient client = new FTPClient();
client.connect(host);
client.login(username, password);
client.changeWorkingDirectory(remotePath);
transportRecursive(client, file);
client.disconnect();
}
public void transportRecursive(FTPClient client, File file) {
if (file.isFile() && file.canRead()) {
client.storeFile(file.getName(), new FileInputStream(file));
} else if (file.isDirectory()) {
client.makeDirectory(file.getName());
client.changeWorkingDirectory(file.getName());
File[] fileList = file.listFiles();
for (File f : fileList) {
transportRecursive(client, f);
}
}
}
對其他傳輸方案也有各自的方案進(jìn)行處理,具體使用哪個 Transporter 的實現(xiàn)被配置在 Spring 風(fēng)格的索引模塊配置文件中。傳輸?shù)姆绞绞庆`活的。比如當(dāng)需要強(qiáng)調(diào)安全性時,我們可以換用基于 SSL 的 FTP 進(jìn)行傳輸。所需要做的只是開發(fā)一個使用 FTP over SSL 的 Transporter 實現(xiàn),并在配置文件中更改 Transporter 的實現(xiàn)即可。
進(jìn)行搜索
在做了這么多之后,我們開始接觸和用戶關(guān)聯(lián)最為緊密的搜索模塊。注意,我們的框架不包括一個基于已經(jīng)收集好的索引進(jìn)行搜索是個很簡單的過程。Lucene 已經(jīng)提供了功能強(qiáng)大的 IndexSearcher 及其子類。在這個部分,我們不會再介紹如何使用這些類,而是關(guān)注在前文提到過的資源元數(shù)據(jù)上。元數(shù)據(jù)從各個資源所在的文件夾中讀取得到,它在搜索模塊中扮演重要的角色。
構(gòu)建一個查詢
對不同資源進(jìn)行搜索的查詢方法并不一樣。例如搜索一個論壇里的所有留言時,我們關(guān)注的一般是留言的標(biāo)題、作者和內(nèi)容;而當(dāng)搜索一個 FTP 站點(diǎn)時,我們更多關(guān)注的是文件名和文件內(nèi)容。另一方面,我們有時可能會使用一個查詢?nèi)ニ阉鞫鄠€資源的結(jié)果。這正是之前我們在前面所提到的元數(shù)據(jù)中 searchableFields 和 resourceName 屬性的作用。前者指出一個資源中哪些字段是參與搜索的;后者則用于在搜索時確定使用哪個或者哪些索引。從技術(shù)細(xì)節(jié)來說,只有有了這些信息,我們才可以構(gòu)造出可用的 Query 對象。
呈現(xiàn)搜索結(jié)果
當(dāng)從 IndexSearcher 對象得到搜索結(jié)果(Hits)之后,當(dāng)然我們可以直接從中獲取需要的值,再格式化予以輸出。但一來格式化輸出搜索結(jié)果(尤其在 Web 應(yīng)用中)是個很常見的需求,可能會經(jīng)常變更;二來結(jié)果的呈現(xiàn)格式應(yīng)該是由分散的資源各自定義,而不是交由搜索模塊來定義。基于上面兩個原因,我們的框架將使用在資源收集端配置結(jié)果輸出格式的方式。這個格式由資源元數(shù)據(jù)中的 hitTextPattern 屬性定義。該屬性是一個字符串類型的值,支持兩種語法
形如 ${field_name} 的子字符串都會被動態(tài)替換成查詢結(jié)果中各個 Document 內(nèi) Field 的值。
形如 $function(...) 的被解釋為函數(shù),括號內(nèi)以逗號隔開的符號都被解釋成參數(shù),函數(shù)可以嵌套。
例如搜索“具體”返回的搜索結(jié)果中包含一個 Document 對象,其 Field 如下表:
Field 名稱 Field 內(nèi)容
url http://example.org/article/1.html
title 示例標(biāo)題
content 這里是具體的內(nèi)容。
那么如果 hitTextPatten 被設(shè)置為“${title}
$highlight(${content}, 5, "", "")”,返回的結(jié)果經(jīng)瀏覽器解釋后可能的顯示結(jié)果如下(這只是個演示鏈接,請不要點(diǎn)擊):
示例標(biāo)題
這里是具體...
上面提到的 $highlight() 函數(shù)用于在搜索結(jié)果中取得最匹配的一段文本,并高亮顯示搜索時使用的短語,其第一個參數(shù)是高亮顯示的文本,第二個參數(shù)是顯示的文本長度,第三和第四個參數(shù)是高亮文本時使用的前綴和后綴。
可以使用正則表達(dá)式和文本解析來實現(xiàn)前面所提到的語法。我們也可以使用 JavaCC 定義 hitTextPattern 的文法,進(jìn)而生成詞法分析器和語法解析器。這是更為系統(tǒng)并且相對而言不易出錯的方法。對 JavaCC 的介紹不是本文的重點(diǎn),您可以在下面的 閱讀資源 中找到學(xué)習(xí)資料。
下面列出的是一些與我們所提出的框架所相關(guān)或者類似的產(chǎn)品,您可以在 學(xué)習(xí)資料 中更多地了解他們。
IBM?OmniFind?Family
OmniFind 是 IBM 公司推出的企業(yè)級搜索解決方案。基于 UIMA (Unstructured Information Management Architecture) 技術(shù),它提供了強(qiáng)大的索引和獲取信息功能,支持巨大數(shù)量、多種類型的文檔資源(無論是結(jié)構(gòu)化還是非結(jié)構(gòu)化),并為 Lotus?Domino?和 WebSphere?Portal 專門進(jìn)行了優(yōu)化。
Apache Solr
Solr 是 Apache 的一個企業(yè)級的全文檢索項目,實現(xiàn)了一個基于 HTTP 的搜索服務(wù)器,支持多種資源和 Web 界面管理,它同樣建立在 Lucene 之上,并對 Lucene 做了很多擴(kuò)展,例如支持動態(tài)字段及唯一鍵,對查詢結(jié)果進(jìn)行動態(tài)分組和過濾等。
Google SiteSearch
使用 Google 的站點(diǎn)搜索功能可以方便而快捷地建立一個站內(nèi)搜索引擎。但是 Google 的站點(diǎn)搜索基于 Google 的網(wǎng)絡(luò)爬蟲,所以無法訪問受保護(hù)的站點(diǎn)內(nèi)容或者 Intranet 上的資源。另外,Google 所支持的資源類型也是有限的,我們無法對其進(jìn)行擴(kuò)展。
SearchBlox?
SearchBlox 是一個商業(yè)的搜索引擎構(gòu)建框架。它本身是一個 J2EE 組件,和我們的框架類似,也支持對網(wǎng)頁和文件系統(tǒng)等資源進(jìn)行索引,進(jìn)而進(jìn)行搜索。
還需考慮的問題
本文介紹的思想試圖利用開源的工具解決中小型應(yīng)用中的常見問題。當(dāng)然,作為一個框架,它還有很多不足,下面列舉出一些可以進(jìn)行改進(jìn)的地方。
性能考慮
當(dāng)需要進(jìn)行索引的資源數(shù)目不多時,隔一定的時間進(jìn)行一次完全索引不會占用很長時間。使用一臺 2G 內(nèi)存,Xeon 2.66G 處理器的服務(wù)器進(jìn)行實際測試,發(fā)現(xiàn)對數(shù)據(jù)庫資源的索引占用的時間很少,一千多條記錄花費(fèi)的時間在 1 秒到 2 秒之內(nèi)。而對 1400 多個文件進(jìn)行索引耗時大約十幾秒。但在大型應(yīng)用中,資源的容量是巨大的,如果每次都進(jìn)行完整的索引,耗費(fèi)的時間會很驚人。我們可以通過跳過已經(jīng)索引的資源內(nèi)容,刪除已不存在的資源內(nèi)容的索引,并進(jìn)行增量索引來解決這個問題。這可能會涉及文件校驗和索引刪除等。
另一方面,框架可以提供查詢緩存來提高查詢效率。框架可以在內(nèi)存中建立一級緩存,并使用如 OSCache 或 EHCache 實現(xiàn)磁盤上的二級緩存。當(dāng)索引的內(nèi)容變化不頻繁時,使用查詢緩存更會明顯地提高查詢速度、降低資源消耗。
分布式索引
我們的框架可以將索引分布在多臺機(jī)器上。搜索資源時,查詢被 flood 到各個機(jī)器上從而獲得搜索結(jié)果。這樣可以免去傳輸索引到某一臺中央服務(wù)器的過程。當(dāng)然也可以在非結(jié)構(gòu)化的 P2P 網(wǎng)絡(luò)上實現(xiàn)分布式哈希表 (DHT),配合索引復(fù)制 (Replication),使得應(yīng)用程序更為安全,可靠,有伸縮性。在閱讀資料中給出了 一篇關(guān)于構(gòu)建分布式環(huán)境下全文搜索的可行性的論文。
安全性
目前我們的框架并沒有涉及到安全性。除了依賴資源本身的訪問控制(如受保護(hù)的網(wǎng)頁和文件系統(tǒng)等)之外,我們還可以從兩方面增強(qiáng)框架本身的安全性:
考慮到一個組織的搜索功能對不同用戶的權(quán)限設(shè)置不一定一樣,可以支持對用戶角色的定義,實行對搜索模塊的訪問控制。
在資源索引模塊中實現(xiàn)一種機(jī)制,讓資源可以限制自己暴露的內(nèi)容,從而縮小索引模塊的索引范圍。這可以類比 robots 文件可以規(guī)定搜索引擎爬蟲的行為。
通過上文的介紹,我們認(rèn)識了一個可擴(kuò)展的框架,由索引模塊和搜索模塊兩部分組成。它可以靈活地適應(yīng)不同的應(yīng)用場景。如果需要更獨(dú)特的需求,框架本身預(yù)留了可以擴(kuò)展的接口,我們可以通過實現(xiàn)這些接口完成功能的定制。更重要的是這一切都是建立在開源軟件的基礎(chǔ)之上。希望本文能為您揭示開源的力量,體驗用開源工具組裝您自己的解決方案所帶來的莫大快樂。
posted @
2009-08-28 15:03 kit_lo 閱讀(1562) |
評論 (0) |
編輯 收藏
第一步:加入log4j-1.2.8.jar到lib下。
第二步:在CLASSPATH下建立log4j.properties。內(nèi)容如下:
1 log4j.rootCategory=INFO, stdout , R
2
3 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
5 log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n
6
7 log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
8 log4j.appender.R.File=D:\Tomcat 5.5\logs\qc.log
9 log4j.appender.R.layout=org.apache.log4j.PatternLayout
10 log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
11
12 log4j.logger.com.neusoft=DEBUG
13 log4j.logger.com.opensymphony.oscache=ERROR
14 log4j.logger.net.sf.navigator=ERROR
15 log4j.logger.org.apache.commons=ERROR
16 log4j.logger.org.apache.struts=WARN
17 log4j.logger.org.displaytag=ERROR
18 log4j.logger.org.springframework=DEBUG
19 log4j.logger.com.ibatis.db=WARN
20 log4j.logger.org.apache.velocity=FATAL
21
22 log4j.logger.com.canoo.webtest=WARN
23
24 log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
25 log4j.logger.org.hibernate=DEBUG
26 log4j.logger.org.logicalcobwebs=WARN
第三步:相應(yīng)的修改其中屬性,修改之前就必須知道這些都是干什么的,在第二部分講解。
第四步:在要輸出日志的類中加入相關(guān)語句:
定義屬性:protected final Log log = LogFactory.getLog(getClass());
在相應(yīng)的方法中:
if (log.isDebugEnabled())
{
log.debug(“System …..”);
}
二、Log4j說明
1 log4j.rootCategory=INFO, stdout , R
此句為將等級為INFO的日志信息輸出到stdout和R這兩個目的地,stdout和R的定義在下面的代碼,可以任意起名。等級可分為OFF、 FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果配置OFF則不打出任何信息,如果配置為INFO這樣只顯示INFO, WARN, ERROR的log信息,而DEBUG信息不會被顯示,具體講解可參照第三部分定義配置文件中的logger。
3 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
此句為定義名為stdout的輸出端是哪種類型,可以是
org.apache.log4j.ConsoleAppender(控制臺),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個日志文件),
org.apache.log4j.RollingFileAppender(文件大小到達(dá)指定尺寸的時候產(chǎn)生一個新的文件)
org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)
具體講解可參照第三部分定義配置文件中的Appender。
4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
此句為定義名為stdout的輸出端的layout是哪種類型,可以是
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串),
org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時間、線程、類別等等信息)
具體講解可參照第三部分定義配置文件中的Layout。
5 log4j.appender.stdout.layout.ConversionPattern= [QC] %p [%t] %C.%M(%L) | %m%n
如果使用pattern布局就要指定的打印信息的具體格式ConversionPattern,打印參數(shù)如下:
%m 輸出代碼中指定的消息
%p 輸出優(yōu)先級,即DEBUG,INFO,WARN,ERROR,F(xiàn)ATAL
%r 輸出自應(yīng)用啟動到輸出該log信息耗費(fèi)的毫秒數(shù)
%c 輸出所屬的類目,通常就是所在類的全名
%t 輸出產(chǎn)生該日志事件的線程名
%n 輸出一個回車換行符,Windows平臺為“rn”,Unix平臺為“n”
%d 輸出日志時間點(diǎn)的日期或時間,默認(rèn)格式為ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
%l 輸出日志事件的發(fā)生位置,包括類目名、發(fā)生的線程,以及在代碼中的行數(shù)。
[QC]是log信息的開頭,可以為任意字符,一般為項目簡稱。
輸出的信息
[TS] DEBUG [main] AbstractBeanFactory.getBean(189) | Returning cached instance of singleton bean 'MyAutoProxy'
具體講解可參照第三部分定義配置文件中的格式化日志信息。
7 log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
此句與第3行一樣。定義名為R的輸出端的類型為每天產(chǎn)生一個日志文件。
8 log4j.appender.R.File=D:\Tomcat 5.5\logs\qc.log
此句為定義名為R的輸出端的文件名為D:\Tomcat 5.5\logs\qc.log
可以自行修改。
9 log4j.appender.R.layout=org.apache.log4j.PatternLayout
與第4行相同。
10 log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
與第5行相同。
12 log4j.logger.com. neusoft =DEBUG
指定com.neusoft包下的所有類的等級為DEBUG。
可以把com.neusoft改為自己項目所用的包名。
13 log4j.logger.com.opensymphony.oscache=ERROR
14 log4j.logger.net.sf.navigator=ERROR
這兩句是把這兩個包下出現(xiàn)的錯誤的等級設(shè)為ERROR,如果項目中沒有配置EHCache,則不需要這兩句。
15 log4j.logger.org.apache.commons=ERROR
16 log4j.logger.org.apache.struts=WARN
這兩句是struts的包。
17 log4j.logger.org.displaytag=ERROR
這句是displaytag的包。(QC問題列表頁面所用)
18 log4j.logger.org.springframework=DEBUG
此句為Spring的包。
24 log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
25 log4j.logger.org.hibernate=DEBUG
此兩句是hibernate的包。
以上這些包的設(shè)置可根據(jù)項目的實際情況而自行定制。
三、log4j詳解
1、定義配置文件
Log4j支持兩種配置文件格式,一種是XML格式的文件,一種是Java特性文件log4j.properties(鍵=值)。下面將介紹使用log4j.properties文件作為配置文件的方法:
①、配置根Logger
Logger 負(fù)責(zé)處理日志記錄的大部分操作。
其語法為:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
其中,level 是日志記錄的優(yōu)先級,分為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者自定義的級別。Log4j建議只使用四個級別,優(yōu) 先級從高到低分別是ERROR、WARN、INFO、DEBUG。通過在這里定義的級別,您可以控制到應(yīng)用程序中相應(yīng)級別的日志信息的開關(guān)。比如在這里定 義了INFO級別,只有等于及高于這個級別的才進(jìn)行處理,則應(yīng)用程序中所有DEBUG級別的日志信息將不被打印出來。ALL:打印所有的日志,OFF:關(guān) 閉所有的日志輸出。 appenderName就是指定日志信息輸出到哪個地方。可同時指定多個輸出目的地。
②、配置日志信息輸出目的地 Appender
Appender 負(fù)責(zé)控制日志記錄操作的輸出。
其語法為:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.optionN = valueN
這里的appenderName為在①里定義的,可任意起名。
其中,Log4j提供的appender有以下幾種:
org.apache.log4j.ConsoleAppender(控制臺),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個日志文件),
org.apache.log4j.RollingFileAppender(文件大小到達(dá)指定尺寸的時候產(chǎn)生一個新的文件),可通過 log4j.appender.R.MaxFileSize=100KB設(shè)置文件大小,還可通過 log4j.appender.R.MaxBackupIndex=1設(shè)置為保存一個備份文件。
org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)
例如:log4j.appender.stdout=org.apache.log4j.ConsoleAppender
定義一個名為stdout的輸出目的地,ConsoleAppender為控制臺。
③、配置日志信息的格式(布局)Layout
Layout 負(fù)責(zé)格式化Appender的輸出。
其語法為:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.optionN = valueN
其中,Log4j提供的layout有以下幾種:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串),
org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時間、線程、類別等等信息)
2、格式化日志信息
Log4J采用類似C語言中的printf函數(shù)的打印格式格式化日志信息,打印參數(shù)如下:
%m 輸出代碼中指定的消息
%p 輸出優(yōu)先級,即DEBUG,INFO,WARN,ERROR,F(xiàn)ATAL
%r 輸出自應(yīng)用啟動到輸出該log信息耗費(fèi)的毫秒數(shù)
%c 輸出所屬的類目,通常就是所在類的全名
%t 輸出產(chǎn)生該日志事件的線程名
%n 輸出一個回車換行符,Windows平臺為“rn”,Unix平臺為“n”
%d 輸出日志時間點(diǎn)的日期或時間,默認(rèn)格式為ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
%l 輸出日志事件的發(fā)生位置,包括類目名、發(fā)生的線程,以及在代碼中的行數(shù)。
3、在代碼中使用Log4j
我們在需要輸出日志信息的類中做如下的三個工作:
1、導(dǎo)入所有需的commongs-logging類:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
2、在自己的類中定義一個org.apache.commons.logging.Log類的私有靜態(tài)類成員:
private final Log log = LogFactory.getLog(getClass());
LogFactory.getLog()方法的參數(shù)使用的是當(dāng)前類的class。
3、使用org.apache.commons.logging.Log類的成員方法輸出日志信息:
if (log.isDebugEnabled())
{
log.debug("111");
}
if (log.isInfoEnabled())
{
log.info("222");
}
if (log.isWarnEnabled())
{
log.warn("333");
}
if (log.isErrorEnabled())
{
log.error("444");
}
if (log.isFatalEnabled())
{
log.fatal("555")
}
posted @
2009-08-28 15:00 kit_lo 閱讀(194518) |
評論 (29) |
編輯 收藏
目標(biāo)驅(qū)動,系統(tǒng)思維,風(fēng)險意識,數(shù)據(jù)量化
凡事預(yù)則立,不預(yù)則廢。如果你不知道要到哪里?給你一張地圖也沒有用。目標(biāo)驅(qū)動首先要有最基本的計劃管理和時間管理能力。對于一個項目,我們過程中做的所有工作都是為了要達(dá)到項目目標(biāo),因此在項目各個階段所有活動都需要考慮對達(dá)成目標(biāo)的影響,當(dāng)發(fā)現(xiàn)偏差后及時糾正。目標(biāo)驅(qū)動讓我們從無目的的事后應(yīng)急變成了有計劃有目的的事前預(yù)測。目標(biāo)驅(qū)動不是要拋棄過程,項目的成功涉及到過程,人和方法工具技術(shù)。為了達(dá)到項目目標(biāo),我們要根據(jù)項目的實際情況采取一系列項目原來已經(jīng)總結(jié)的最佳實踐形成一套過程,高效的過程和積極心態(tài)的人是保證項目目標(biāo)達(dá)成的關(guān)鍵。因此作為項目經(jīng)理要時刻問自己,項目的目標(biāo)是什么?項目當(dāng)前狀態(tài)和我達(dá)成目標(biāo)的差距是什么?我如何解決和應(yīng)對。
項目的成功受到多方面的因素的影響,而且各個因素之間還存在正反作用力。系統(tǒng)思維就是要讓我們能夠清楚的認(rèn)識到影響項目目標(biāo)和成功的各個要素,以及它們之間存在的關(guān)系。形成一種適合項目的動態(tài)系統(tǒng)模型,通過這個動態(tài)模型去平衡項目各方干系人的利益,平衡項目四要素之間的關(guān)系,平衡項目的短期和長期的利益。項目經(jīng)理的一個重要能力就是平衡,沒有最優(yōu)解,只有滿意解,懂得了平衡就知道當(dāng)項目出現(xiàn)變更和調(diào)整的時候如何更好的應(yīng)對。從單要素最優(yōu)的單向思維過渡到關(guān)注整個系統(tǒng)的全局思維模式上。
風(fēng)險意識簡單來講就是項目在執(zhí)行過程中可能發(fā)生的各種問題我都事先預(yù)見到了而采取了適當(dāng)?shù)木徑獯胧@樣才能夠真正的讓項目能夠按照預(yù)先制定的計劃和目標(biāo)進(jìn)行。再簡單點(diǎn)就是如果風(fēng)險管理做得好,項目是不應(yīng)該失敗的。君子安而不忘危,存而不忘亡,治而不忘亂。風(fēng)險管理的重點(diǎn)正是在于要形成風(fēng)險意識,要能夠通過歷史經(jīng)驗的積累,能夠把項目的關(guān)鍵風(fēng)險識別出來,使項目能夠從事后的救火轉(zhuǎn)變到事前的防備,使項目能夠在前面緊張后面輕松。
要談及量化管理首先應(yīng)該要培訓(xùn)用數(shù)據(jù)說話的分析思維。在軟件項目管理中我們做度量的目的,就是要收集和分析各種歷史數(shù)據(jù),通過對數(shù)據(jù)的分析來知道項目真正的效率,同時為后續(xù)項目提供各種估算參數(shù)數(shù)據(jù)。以數(shù)據(jù)說話讓我們從全憑主觀經(jīng)驗臆斷轉(zhuǎn)變到對事物的客觀數(shù)據(jù)分析上。只有能夠收集數(shù)據(jù),分析數(shù)據(jù)我們才可能持續(xù)改進(jìn)。有了數(shù)據(jù)意識后就是要有統(tǒng)計和量化管理方面的意識,利用統(tǒng)計學(xué)的思維和量化管理手段不僅僅是讓我們的過程穩(wěn)定和受控制,能夠去發(fā)現(xiàn)項目執(zhí)行過程中特殊原因引起的波動,針對特殊波動進(jìn)行根源分析并采取糾正行動;還能夠讓我們能夠根據(jù)預(yù)測模型更加準(zhǔn)確的預(yù)測項目能夠達(dá)成目標(biāo)的程度和概率。
----------------------------------------
努力使自己成為工作的終結(jié)者
posted @
2009-08-28 14:53 kit_lo 閱讀(463) |
評論 (1) |
編輯 收藏
大家喝的是啤酒。這時你入座了。
你給自己倒了杯可樂,這叫低配置。
你給自己倒了杯啤酒,這叫標(biāo)準(zhǔn)配置。
你給自己倒了杯茶水,這茶的顏色還跟啤酒一樣,這叫木馬。
你給自己倒了杯可樂,還滴了幾滴醋,不僅顏色跟啤酒一樣,而且不冒熱氣還有泡泡,這叫超級木馬。
你的同事給你倒了杯白酒,這叫推薦配置。
人到齊了,酒席開始了。
你先一個人喝了一小口,這叫單元測試。
你跟旁邊的人說哥們咱們隨意,這叫交叉測試。
但是他說不行,這杯要干了,這叫壓力測試。
于是你說那就大家一起來吧,這叫內(nèi)部測試。
這個時候boss向全場舉杯了,這叫集成測試。
菜過三巡,你就不跟他們客氣了。
你向?qū)γ娴娜司淳疲@叫p2p.
你向?qū)γ娴娜司淳疲鼐茨悖阌衷倬此?#8230;…,這叫tcp.
你向一桌人挨個敬酒,這叫令牌環(huán)。
你說只要是兄弟就干了這杯,這叫廣播。
可是你的女上司聽了不高興了:只有兄弟么,罰酒三杯。這叫炸彈。
可是你的女下屬聽了不高興了:我喝一口,你喝一杯,這叫惡意攻擊。
有一個人過來向這桌敬酒,你說不行你先過了我這關(guān),這叫防火墻。
你的小弟們過來敬你酒,這叫一對多。
你是boss,所有人過來敬你酒,這叫服務(wù)器。
酒是一樣的,可是喝法是不同的。
你喝了一杯,boss喝了一口,這叫c#。
你喝了一杯,mm喝了一口,這叫vb。
你喝了一杯,你大哥喝了半杯,這叫c++。
你喝了半杯,你小弟喝了一杯,這叫匯編。
你喝了一杯,你的搭檔也喝了一杯,這叫c。
酒是一樣的,可是喝酒的人是不同的。
你越喝臉越紅,這叫資源釋放。
你越喝臉越白,這叫資源獨(dú)占。
你已經(jīng)醉了,卻說我還能喝,叫做虛擬內(nèi)存。
你明明能喝,卻說我已經(jīng)醉了,叫做資源保留。
你喝一段時間就上廁所,這叫cache。
酒過三巡,你也該活動活動了。
你一桌一桌的走,這叫輪巡。
你突然看到某一桌的漂亮mm,走了過去,這叫激活事件。
你去了坐下來就不打算走了,這叫死循環(huán)。
你的老大舉杯邀你過去,你只好過去,這叫優(yōu)先級。
你向一桌敬酒,他們說不行不行我們都喝白的,于是你也喝白的,這叫本地化。
你向boss敬酒,可是boss被圍了起來,你只能站在外圈,這叫隊列。
你終于到了內(nèi)圈,小心翼翼的向前一步,這叫訪問臨界區(qū)。
你拍著boss的肩膀說哥們咱們喝一杯,這叫越界。
你不知喝了幾圈了,只會說兩個字,干了,這叫udp。
可是還有人拿著酒瓶跑過來說,剛才都沒跟你喝,這叫丟包。
喝酒喝到最后的結(jié)果都一樣
你突然跑向廁所,這叫捕獲異常錯誤。
你在廁所吐了,反而覺得狀態(tài)不錯,這叫釋放內(nèi)存。
你在臺面上吐了,覺得很慚愧,這叫時實錯誤。
你在boss面前吐了,覺得很害怕,這叫災(zāi)難性錯誤。
你吐到了boss身上,只能索性暈倒了,這叫Shut Down。
posted @
2009-08-28 14:52 kit_lo 閱讀(466) |
評論 (1) |
編輯 收藏