1 Jetty與glassfish的基本介紹
1.1 研究背景及意義
下圖是對幾個主流的應用服務器使用比率的粗率統計結果做出的一個餅圖。這個圖的數據也許不夠精確,但它還是可以在一定程度上反映我們web項目對各類應用服務器的一些選擇趨勢。我們可以看到,Tomcat占據了主要的地位,但是它并不孤獨,有超過一半以上的應用并沒有使用tomcat作為web容器。這是針對每個項目自身特點做出的選擇,也許我們無法比較出哪一款是最好的應用服務器,但是,我們可以在眾多的應用服務器中,做出一些性能上的測試和比較,選擇一款最適合自己的項目的應用服務器。
本次的報告中,我選擇了較為受關注的jetty以及稍微冷門一點的glassfish作為研究對象,對它們在windows和linux上分別進行了App項目的部署和簡單的測試,希望這個文檔能對以后的應用服務器研究提供一些簡單的參考。

1.2 Jetty基本介紹
Jetty是一款由純JAVA語言編譯的實現servlet規范的應用服務器。它是eclipse基金會支持的一款軟件。打開它的目錄你會發現很有趣的事情,它的啟動程序是一個叫start.jar的文件,并且需要在cmd中執行命令:java –jar start.jar。這對于習慣于tomcat等其他以bat(windows)或者sh(linux)文件啟動的使用者來說,是一個小小的新體驗。
Jetty的很多特性在網絡上都有很詳細的記載,在文檔中我不想做任何的簡單復制或者引用,那些資料大家可以去百度搜索。這里只提它的一點最明顯的優勢:嵌入式服務器。Jetty之所以能占據一個不錯的使用比率,也正是因為它的這個特性。它可以在java程序中作為一個servlet讓java程序進行調用。這使它更多的是作為兩臺機器之間的通信服務應用。它的這種特性,可以省去寫socket等通信程序的煩惱,在兩臺機器的那些非標準的servlet程序,直接以http請求的形式,調用jetty方法,形成一個標準的符合servlet規范的通信。當然,它作為web容器的性能也是十分優秀的,后面的測試中,我將會提到。但是,作為一個互聯網公司,目前并不存在這方面的邏輯代碼需要整改成jetty形式的嵌入服務,我并沒有對它怎么嵌入使用去做更進一步的研究。
1.3 Glassfish基本介紹

這是一個讓我覺得使用起來十分便捷人性的應用服務器,最開始選擇研究glassfish的原因,是因為它是一款開源的相對于tomcat對熱部署支持的十分好的應用服務器。因此,我也將它集成到了eclipse中,進行熱部署的測試。如果開發環境的應用服務器可以換成glassfish的話,也許可以節約很多由于反復重啟tomcat而浪費的時間。
它是執行一個叫asadmin.bat的文件進行啟動。在執行asadmin.bat后會彈出一個命令框,我們在里面輸入start-domain domain1(這個名字跟你自己的domains下的domain目錄名有關)
啟動后,我們在地址欄輸入localhost:4848就可以進入它的管理界面了。


通過它的這個界面可以對項目進行部署,還有數據庫連接等進行管理還有集群等配置,甚至還有對數據和項目運行情況的監控管理,相對于tomcat,glassfish的這個功能可以減少使用者的使用難度,更加直觀地配置應用服務器,更好的優化應用服務器的性能。Glassfish簡單的服務器性能比較,將在后面中提到。
2 jetty,glassfish,tomcat的性能測試
2.1 測試環境說明
本次測試將分別在windows和linux環境下進行測試。測試將在兩個環境中使用的是開發環境,數據庫連接的是開發庫,測試項目為自研APP。操作系統使用的是windows7旗艦版和centos7 server。計算機的基本情況如下圖所示:

使用的壓力測試軟件均為:JMeter
測試所使用的請求地址連接為:
/App/users/getUserInfo?uid=11001&v=1.0.0
/App/uses/doLogin?uName=test&pwd=pass@123
/App/menus/getModuleMenu?uid=11001&urole=0&uSource=0
測試將隨機訪問這三個請求。三個請求分別為云服務的相關請求。三個請求對應三種不同類型的服務,具有很強的業務代表性。
在測試結果中我們將看到幾個參考值,下面給出每個值的意義。
樣本數目:總共發送到服務器的請求數。
最新樣本:代表時間的數字,是服務器響應最后一個請求的時間。
吞吐量:服務器每分鐘處理的請求數。
平均值:總運行時間除以發送到服務器的請求數。
中間值:時間的數字,有一半的服務器響應時間低于該值而另一半高于該值。
偏離:服務器響應時間變化、離散程度測量值的大小,或者,換句話說,就是數據的分布,這個值越小越好。
2.2 Windows7環境性能測試
使用jdk版本:1.7.0_80
2.2.1 Jetty測試
100線程測試(1)
測試的端口為8090,線程數為100,循環次數為10線程間請求的允許的間隔時間為10,也就是10秒鐘內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為35164ms
100線程測試(2)
測試端口為8090,線程數為100,循環次數為10線程間請求的允許的間隔時間為5,也就是5秒鐘建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為35473ms
100線程測試(3)
測試端口為8090,線程數為100,循環次數為10線程間請求的允許的間隔時間為0,也就是立即建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為32037ms
50線程測試(1)
測試端口為8090,線程數為50,循環次數為20線程間請求的允許的間隔時間為0,也就是立即建立50個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為23171ms
10線程測試(1)
測試端口為8090,線程數為10,循環次數為100線程間請求的允許的間隔時間為0,也就是立即建立10個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為5675ms
2.2.2 Glassfish測試
100線程測試(1)
測試的端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為10,也就是10秒鐘內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為25126ms
100線程測試(2)
測試端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為5,也就是5秒內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為23848ms
100線程測試(3)
測試端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為0,也就是有100個線程同時發起請求。下面是測試結果:

可以看到請求平均處理響應時間為27552ms
50線程測試(1)
測試端口為8080,線程數為50,循環次數為20線程間請求的允許的間隔時間為0,也就是立即建立50個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為12836ms
10線程測試(1)
測試端口為8090,線程數為10,循環次數為100線程間請求的允許的間隔時間為0,也就是立即建立10個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為7277ms
2.2.3 Tomcat測試
100線程測試(1)
測試的端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為10,也就是10秒鐘內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為39581ms
100線程測試(2)
測試端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為5,也就是5秒內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為32685ms
100線程測試(3)
測試端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為0,也就是有100個線程同時發起請求。下面是測試結果:

可以看到請求平均處理響應時間為33888ms
50線程測試(1)
測試端口為8080,線程數為50,循環次數為20線程間請求的允許的間隔時間為0,也就是立即建立50個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為26328ms
10線程測試(1)
測試端口為8090,線程數為10,循環次數為100線程間請求的允許的間隔時間為0,也就是立即建立10個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為4334ms
此外,在3個應用服務器測試期間,由于同時處理的請求數過多,均發生了如下異常,提示創建的對象過多,內存不足。這跟機器環境,應用服務器設置以及JVM設置有關,但也有可能我們的APP也許隱藏著某些對象沒有被垃圾回收機制回收的問題(只是猜測)。

2.3 CentOS7 server環境性能測試
使用JDK版本1.7.0_25
請求地址為192.168.32.129
2.3.1 Jetty測試
100線程測試(1)
測試的端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為10,也就是10秒鐘內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為28351ms
100線程測試(2)
測試端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為5,也就是5秒鐘建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為29166ms
100線程測試(3)
測試端口為8080,線程數為100,循環次數為10線程間請求的允許的間隔時間為0,也就是立即建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為28267ms
50線程測試(1)
測試端口為8080,線程數為50,循環次數為20線程間請求的允許的間隔時間為0,也就是立即建立50個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為20784ms
10線程測試(1)
測試端口為8080,線程數為10,循環次數為100線程間請求的允許的間隔時間為0,也就是立即建立10個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為3139ms
2.3.2 Glassfish測試
100線程測試(1)
測試的端口為8088,線程數為100,循環次數為10線程間請求的允許的間隔時間為10,也就是10秒鐘內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為28697ms
100線程測試(2)
測試端口為8088,線程數為100,循環次數為10線程間請求的允許的間隔時間為5,也就是5秒內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為28260ms
100線程測試(3)
測試端口為8088,線程數為100,循環次數為10線程間請求的允許的間隔時間為0,也就是有100個線程同時發起請求。下面是測試結果:

可以看到請求平均處理響應時間為28675ms
50線程測試(1)
測試端口為8088,線程數為50,循環次數為20線程間請求的允許的間隔時間為0,也就是立即建立50個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為26809ms
10線程測試(1)
測試端口為8088,線程數為10,循環次數為100線程間請求的允許的間隔時間為0,也就是立即建立10個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為6110ms
2.3.3 Tomcat測試
100線程測試(1)
測試的端口為8888,線程數為100,循環次數為10線程間請求的允許的間隔時間為10,也就是10秒鐘內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為30078ms
100線程測試(2)
測試端口為8888,線程數為100,循環次數為10線程間請求的允許的間隔時間為5,也就是5秒內建立100個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為32510ms
100線程測試(3)
測試端口為8888,線程數為100,循環次數為10線程間請求的允許的間隔時間為0,也就是立即有100個線程同時發起請求。下面是測試結果:

可以看到請求平均處理響應時間為30871ms
50線程測試(1)
測試端口為8888,線程數為50,循環次數為20線程間請求的允許的間隔時間為0,也就是立即建立50個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為25443ms
10線程測試(1)
測試端口為8888,線程數為10,循環次數為100線程間請求的允許的間隔時間為0,也就是立即建立10個線程發起請求。下面是測試結果:

可以看到請求平均處理響應時間為2745ms
3 討論與總結
在第2章給出了豐富的測試數據。測試的重點以高并發的情況為主,也就是100線程的情況,分別每個應用服務器都做了三次測試。我們對比這些測試數據,可以看出無論是在windows還是在linux環境下,glassfish對高并發的處理比jetty和tomcat都要好一些,jetty與tomcat對高并發的處理能力相比相差不大。而在50線程并發請求的時候,我們發現這個差距縮小了,直到在10線程這種低并發請求的情況中,三個應用服務器的處理性能卻又反了過來。我們可以清楚地看到,tomcat的處理能力比jetty更好,而glassfish的處理能力變得較弱。與此同時,我們還可以看到相對于windows7,CentOS7server中應用服務器的表現更加出色,響應時間更短,即使centos7server只是運行在虛擬機之中。
應用服務器的性能需要參考的數據遠遠不是一個并發請求響應時間能解決的,還有對jetty與glassfish的部署與設置,每個應用服務器的標準都不同,例如jetty對servlet標準的嚴格檢查,令我們在glassfish和tomcat下能運行的項目,在jetty中都不能部署起來。例如glassfish,對高并發的情景處理的較好,擁有一個十分強大的項目和服務器管理程序,而這以為的優點卻也換來了處理一般情況的能力卻不如其他應用服務器的缺點。又如我在第一章所介紹的,為什么處在中庸的jetty在2014年統計的應用服務器選擇上,占有率能排第二,因為它的特點更加地聚焦于成為一個嵌入式的服務器,將一些非原本需要通過socket或者其他方式連接的非web業務邏輯封裝成一個servlet。結合實際來說就像我們正在使用的上傳編譯工具,有一部分文件處理和調用cmd命令的代碼便是用socket與我們的java服務端連接,這種形式的連接,使用jetty便可以輕松的替換,并且不用考慮更多的字符編碼問題,以及socket的編寫和參數的處理。這次的研究,對于三種應用服務器無法評價出哪一款更適合我們的APP,因為還有很多的因素需要去參考。例如,穩定性,安全性,伸縮性等等。
最后,我需要說明的是,這些測試數據只能提供一個簡單的參考。這些數據是在本地開發環境的數據庫與我自己的電腦環境得到的,因此這些結論只能當作一個趨勢的參考。真正生產環境的服務器性能測試也許會和本次測試有出入,并且如果要正真的模擬服務器的整個工作,我們所選取的隨機請求也不能只是三個有代表性的連接,而應該是通過實際環境每個功能被請求的次數,來模擬真正的服務器性能測試,以達到一個更為精確的結果,讓我們可以從中來調優我們的應用服務器,以及選取更好的server環境。