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