
本文已經(jīng)發(fā)表于InfoQ中文站,(
大型Java Web項(xiàng)目的架構(gòu)和部署問題)
一位ID是jackson1225的網(wǎng)友在javaeye詢問了一個(gè)大型Web系統(tǒng)的架構(gòu)和部署選型問題,希望能提高現(xiàn)有的基于Java的Web應(yīng)用的服務(wù)能力。由于架構(gòu)模式和部署調(diào)優(yōu)一直是Java社區(qū)的熱門話題,這個(gè)問題引發(fā)了很多熱心網(wǎng)友的討論,其中一些意見對(duì)其它大型Web項(xiàng)目也有很好的指導(dǎo)意義。在討論之初jackson1225這樣描述了當(dāng)前的應(yīng)用的架構(gòu)和部署方案:
目前系統(tǒng)架構(gòu)如下:
- web層采用struts+tomcat實(shí)現(xiàn),整個(gè)系統(tǒng)采用20多臺(tái)web服務(wù)器,其負(fù)載均衡采用硬件F5來實(shí)現(xiàn);
- 中間層采用無(wú)狀態(tài)會(huì)話Bean+DAO+helper類來實(shí)現(xiàn),共3臺(tái)weblogic服務(wù)器,部署有多個(gè)EJB,其負(fù)載均衡也采用F5來實(shí)現(xiàn);
- 數(shù)據(jù)庫(kù)層的操作是自己寫的通用類實(shí)現(xiàn)的,兩臺(tái)ORACLE數(shù)據(jù)庫(kù)服務(wù)器,分別存放用戶信息和業(yè)務(wù)數(shù)據(jù);一臺(tái)SQL SERVER數(shù)據(jù)庫(kù),是第三方的業(yè)務(wù)數(shù)據(jù)信息;
web層調(diào)用EJB遠(yuǎn)程接口來訪問中間件層。web層首先通過一個(gè)XML配置文件中配置的EJB接口信息來調(diào)用相應(yīng)的EJB遠(yuǎn)程接口;
該系統(tǒng)中一次操作涉及到兩個(gè)ORACLE庫(kù)以及一個(gè)SQL SERVER庫(kù)的訪問和操作,即有三個(gè)數(shù)據(jù)庫(kù)連接,在一個(gè)事務(wù)中完成。
這樣的架構(gòu)其實(shí)很多公司都在使用,因?yàn)镾truts和Tomcat分別是最流行的Java Web MVC框架和Servlet容器,而F5公司的負(fù)載均衡是橫向擴(kuò)展常見的解決方案(例如配置session sticky方案)。由于這個(gè)系統(tǒng)中有跨數(shù)據(jù)源的事務(wù),所以使用Weblogic Server EJB容器和支持兩階段提交的數(shù)據(jù)庫(kù)驅(qū)動(dòng)就可以保證跨數(shù)據(jù)源的事物完整性(當(dāng)然,容器管理的分布式事務(wù)并非是唯一和最優(yōu)的解決方案)。
但是隨著Rod Johnson重量級(jí)的著作《J2EE Development without EJB》和其中的Spring框架的流行,輕量級(jí)框架和輕量級(jí)容器的概念已經(jīng)深入人心。所以對(duì)于jackson1225提出的這個(gè)場(chǎng)景,大多數(shù)網(wǎng)友都提出了置疑,認(rèn)為這個(gè)系統(tǒng)濫用了技術(shù),完全是在浪費(fèi)錢。網(wǎng)友們大都認(rèn)為SLSB(無(wú)狀態(tài)會(huì)話Bean)完全沒有必要出現(xiàn)在這個(gè)場(chǎng)景中,認(rèn)為SLSB通過遠(yuǎn)程接口訪問本地資源會(huì)有很大的性能開銷,這種觀點(diǎn)也是Rod johnson在without EJB中批判EJB 2.x中的一大反模式。
由于JavaEE是一個(gè)以模式見長(zhǎng)的解決方案,模式和架構(gòu)在JavaEE中占有很重要的地位,所以很多業(yè)內(nèi)專家也都警惕“反模式(Anti-patterns)”的出現(xiàn)。對(duì)于上面所述的方案是否是反模式,jackson1225馬上站出來申辯:
我們項(xiàng)目就是把EJB作為一個(gè)Facade,只是提供給WEB層調(diào)用的遠(yuǎn)程接口,而且只用了無(wú)狀態(tài)會(huì)話Bean,所以性能上還可以的。
這個(gè)解釋很快得到了一些網(wǎng)友的認(rèn)可,但是大家很快意識(shí)到架構(gòu)的好壞決定于是否能夠滿足用戶的需求,davexin(可能是jackson1225的同事)描述了這個(gè)系統(tǒng)的用戶和并發(fā)情況:
現(xiàn)在有用戶4000萬(wàn),馬上要和另一個(gè)公司的會(huì)員系統(tǒng)合并,加起來一共有9000萬(wàn)用戶。數(shù)據(jù)量單表中有一億條以上的數(shù)據(jù)。這是基本的情況,其實(shí)我覺得現(xiàn)在的架構(gòu)還是可以的,現(xiàn)在支持的并發(fā)大概5000并發(fā)用戶左右,接下來會(huì)進(jìn)行系統(tǒng)改造,目標(biāo)支持1萬(wàn)個(gè)并發(fā)用戶。
具體的并發(fā)量公布后又有網(wǎng)友置疑這個(gè)數(shù)據(jù),認(rèn)為這個(gè)系統(tǒng)的Servlet容器支持的并發(fā)數(shù)太小,懷疑是否配置不夠優(yōu)化。davexin又補(bǔ)充了該項(xiàng)目的服務(wù)器配置:
系統(tǒng)前端tomcat都是用的刀片,配置在2G內(nèi)存,cpu大概在2.0G,每臺(tái)機(jī)器也就支持250-400個(gè)并發(fā),再多的話,就會(huì)相應(yīng)時(shí)間非常的常,超過20秒,失去了意義 ,所以我們才得出這樣的結(jié)論的。
一位ID是cauherk的網(wǎng)友提出了比較中肯的意見,他沒有從Web容器單純的并發(fā)支持能力上提出改進(jìn)方案,而是提出了對(duì)于類似的應(yīng)用的一些通用的改進(jìn)提示,這里摘要一下:
- 數(shù)據(jù)庫(kù)壓力問題
可以按照業(yè)務(wù)、區(qū)域等等特性對(duì)數(shù)據(jù)庫(kù)進(jìn)行配置,可以考慮分庫(kù)、使用rac、分區(qū)、分表等等策略,確保數(shù)據(jù)庫(kù)能正常的進(jìn)行交易。
- 事務(wù)問題
要在兩個(gè)數(shù)據(jù)庫(kù)中操作,那么必須考慮到分布式事務(wù)。你應(yīng)該仔細(xì)的設(shè)計(jì)你的系統(tǒng),來避免使用分布式事務(wù),以避免分布式事務(wù)帶來更多的數(shù)據(jù)庫(kù)壓力和其它問題。推薦你采用延遲提交的策略(并不保證數(shù)據(jù)的完整),來避免分布式事務(wù)的問題,畢竟commit失敗的幾率很低。
- web的優(yōu)化
將靜態(tài)、圖片獨(dú)立使用不同的服務(wù)器,對(duì)于常態(tài)的靜態(tài)文件,采用E-TAG或者客戶端緩存, google很多就是這樣干的。對(duì)于熱點(diǎn)的功能,考慮使用完全裝載到內(nèi)存,保證絕對(duì)的響應(yīng)速度,對(duì)于需要頻繁訪問的熱點(diǎn)數(shù)據(jù),采用集中緩存(多個(gè)可以采用負(fù)載均衡),減輕數(shù)據(jù)庫(kù)的壓力。
對(duì)于幾乎除二進(jìn)制文件,都應(yīng)該在L4上配置基于硬件的壓縮方案,減少網(wǎng)絡(luò)的流量。提高用戶使用的感知。
- 網(wǎng)絡(luò)問題
可以考慮采用鏡像、多路網(wǎng)絡(luò)接入、基于DNS的負(fù)載均衡。如果有足夠的投資,可以采用CDN(內(nèi)容分發(fā)網(wǎng)),減輕你的服務(wù)器壓力。
cauherk的這個(gè)分析比較到位,其中ETags的方案是最近的一個(gè)熱點(diǎn),InfoQ的“使用ETags減少Web應(yīng)用帶寬和負(fù)載”里面對(duì)這種方案有很詳細(xì)的介紹。一般以數(shù)據(jù)庫(kù)為中心的Web應(yīng)用的性能瓶頸都在數(shù)據(jù)庫(kù)上,所以cauherk把數(shù)據(jù)庫(kù)和事務(wù)問題放到了前兩位來討論。但是davexin解釋在所討論的這個(gè)項(xiàng)目中數(shù)據(jù)庫(kù)并非瓶頸:
我們的壓力不在數(shù)據(jù)庫(kù)層,在web層和F5。 當(dāng)高峰的時(shí)候 ,F(xiàn)5也被點(diǎn)死了,就是每秒點(diǎn)擊超過30萬(wàn),web動(dòng)態(tài)部分根本承受不了。根據(jù)我們程序記錄,20臺(tái)web最多承受5000個(gè)并發(fā),如果再多,tomcat就不響應(yīng)了。就像死了一樣。
這個(gè)回復(fù)讓接下來的討論都集中于Web容器的性能優(yōu)化,但是JavaEye站長(zhǎng)robbin發(fā)表了自己的意見,將話題引回了這個(gè)項(xiàng)目的架構(gòu)本身:
performance tuning最重要的就是定位瓶頸在哪里,以及瓶頸是怎么產(chǎn)生的。
我的推測(cè)是瓶頸還是出在EJB遠(yuǎn)程方法調(diào)用上!
tomcat上面的java應(yīng)用要通過EJB遠(yuǎn)程方法調(diào)用,來訪問weblogic上面的無(wú)狀態(tài)SessionBean,這樣的遠(yuǎn)程方法調(diào)用一般都在100ms~500ms級(jí)別,或者更多。而如果沒有遠(yuǎn)程方法調(diào)用,即使大量采用spring的動(dòng)態(tài)反射,一次完整的web請(qǐng)求處理在本地JVM內(nèi)部的完成時(shí)間一般也不過20ms而已。一次web請(qǐng)求需要過長(zhǎng)的執(zhí)行時(shí)間,就會(huì)導(dǎo)致servlet線程被占用更多的時(shí)間,從而無(wú)法及時(shí)響應(yīng)更多的后續(xù)請(qǐng)求。
如果這個(gè)推測(cè)是成立的話,那么我的建議就是既然你沒有用到分布式事務(wù),那么就干脆去掉EJB。weblogic也可以全部撤掉,業(yè)務(wù)層使用spring取代EJB,不要搞分布式架構(gòu),在每個(gè)tomcat實(shí)例上面部署一個(gè)完整的分層結(jié)構(gòu)。
另外在高并發(fā)情況下,apache處理靜態(tài)資源也很耗內(nèi)存和CPU,可以考慮用輕量級(jí)web server如lighttpd/litespeed/nginx取代之。
robbin的推斷得到了網(wǎng)友們的支持,davexin也認(rèn)同robbin的看法,但是他解釋說公司認(rèn)為放棄SLSB存在風(fēng)險(xiǎn),所以公司傾向于通過將Tomcat替換為Weblogic Server 10來提升系統(tǒng)的用戶支撐能力。robbin則馬上批評(píng)了這種做法:
坦白說我還從來沒有聽說過大規(guī)模互聯(lián)網(wǎng)應(yīng)用使用EJB的先例。為什么大規(guī)模互聯(lián)網(wǎng)應(yīng)用不能用EJB,其實(shí)就是因?yàn)镋JB性能太差,用了EJB幾乎必然出現(xiàn)性能障礙。
web容器的性能說到底無(wú)非就是Servlet線程調(diào)度能力而已,Tomcat不像WebLogic那樣附加n多管理功能,跑得快很正常。對(duì)比測(cè)試一下WebLogic的數(shù)據(jù)庫(kù)連接池和C3P0連接池的性能也會(huì)發(fā)現(xiàn)類似的結(jié)論,C3P0可要比WebLogic的連接池快好幾倍了。這不是說WebLogic性能不好,只不過weblogic要實(shí)現(xiàn)更多的功能,所以在單一的速度方面就會(huì)犧牲很多東西。
以我的經(jīng)驗(yàn)來判斷,使用tomcat5.5以上的版本,配置apr支持,進(jìn)行必要的tuning,使用BEA JRockit JVM的話,在你們目前的刀片上面,支撐500個(gè)并發(fā)完全是可以做到的。結(jié)合你們目前20個(gè)刀片的硬件,那么達(dá)到1萬(wàn)并發(fā)是沒問題的。當(dāng)然這樣做的前提是必須扔掉EJB,并置web層和業(yè)務(wù)層在同一個(gè)JVM內(nèi)部。
接下來robbin還針對(duì)davexin對(duì)話題中的應(yīng)用分別在tomcat和weblogic上的測(cè)試數(shù)據(jù)進(jìn)行了分析:
引用:
2。1臺(tái)weblogic10 Express(相當(dāng)于1臺(tái)tomcat,用于發(fā)布jsp應(yīng)用)加1臺(tái)weblogic10(發(fā)布ejb應(yīng)用),能支持1000個(gè)并發(fā)用戶......
......
4。1臺(tái)tomcat4.1加1臺(tái)weblogic8,只能支持350個(gè)并發(fā)用戶,tomcat就連結(jié)超時(shí),說明此種結(jié)構(gòu)瓶頸在tomcat。
這說明瓶頸還不在EJB遠(yuǎn)程調(diào)用上,但是問題已經(jīng)逐漸清楚了。為什么weblogic充當(dāng)web容器發(fā)起遠(yuǎn)程EJB調(diào)用的時(shí)候可以支撐1000個(gè)并發(fā),但是tomcat只能到350個(gè)?只有兩個(gè)可能的原因:
- 你的tomcat沒有配置好,嚴(yán)重影響了性能表現(xiàn)
- tomcat和weblogic之間的接口出了問題
接著springside項(xiàng)目發(fā)起者江南白衣也提出了一個(gè)總體的優(yōu)化指導(dǎo):
1.基礎(chǔ)配置優(yōu)化
tomcat 6? tomcat參數(shù)調(diào)優(yōu)?
JRockit JVM? JVM參數(shù)調(diào)優(yōu)?
Apache+Squid 處理靜態(tài)內(nèi)容?
2.業(yè)務(wù)層優(yōu)化
部分功能本地化,而不調(diào)remote session bean?
異步提交操作,JMS?
cache熱點(diǎn)數(shù)據(jù)?
3.展示層優(yōu)化
動(dòng)態(tài)頁(yè)面發(fā)布為靜態(tài)頁(yè)面?
Cache部分動(dòng)態(tài)頁(yè)面內(nèi)容?
davexin在調(diào)整了Tomcat配置后應(yīng)驗(yàn)了robbin對(duì)tomcat配置問題的質(zhì)疑,davexin這樣描述經(jīng)過配置優(yōu)化以后的測(cè)試結(jié)果:
經(jīng)過測(cè)試,并發(fā)人數(shù)是可以達(dá)到像robbin所說的一樣,能夠在600人左右,如果壓到并發(fā)700人,就有15%左右的失敗,雖然在調(diào)整上面參數(shù)之后,并發(fā)人數(shù)上去了,但是在同樣的時(shí)間內(nèi)所完成的事務(wù)數(shù)量下降了10%左右,并且響應(yīng)時(shí)間延遲了1秒左右,但從整體上來說,犧牲一點(diǎn)事務(wù)吞吐量和響應(yīng)時(shí)間,并發(fā)人數(shù)能夠提高500,覺得還是值得的。
至此這個(gè)話題有了一個(gè)比較好的結(jié)果。這個(gè)話題并非完全針對(duì)一個(gè)具體的項(xiàng)目才有意義,更重要的是在分析和討論問題的過程中網(wǎng)友們解決問題的思路,尤其是cauherk、robbin、江南白衣等幾位網(wǎng)友提出的意見可以讓廣大Java Web項(xiàng)目開發(fā)者了解到中、大型項(xiàng)目所需要考慮的架構(gòu)和部署所需要考慮的關(guān)鍵問題,也消除了很多人對(duì)輕量Servlet容器與EJB容器性能的一些誤解。
在討論中還有一些小插曲,如davexin和江南白衣討論了JRocket的實(shí)時(shí)(Realtime)版本是否可以提升Servlet容器的相應(yīng)能力,答案是不可以。還有ID為mfc42d的網(wǎng)友從Servlet容器的并發(fā)支持能力引申到了Java的線程調(diào)度能力和NIO對(duì)Servelet容器的意義,他推薦了自己的兩篇不錯(cuò)的blog“java的線程實(shí)現(xiàn)”和“java進(jìn)程使用的最大內(nèi)存的數(shù)值”,blog文章里面從JVM源碼級(jí)別分析了Java的線程支持能力,面臨JVM性能調(diào)優(yōu)問題的網(wǎng)友可以認(rèn)真閱讀一下。