新一代服務器性能測試工具Gatling
21世紀是云的世紀, 大規模云網已經出現了,而且在未來幾年內會得到高速發展,從而使得基于云的系統也會越來越多。如果要開發一款高性能的云系統,服務器性能測試是一個必不可少的環節。今天,就來介紹一款新一代服務器性能測試工具Gatling。 一,什么是Gatling Gatling是一款基于Scala 開發的高性能服務器性能測試工具,它主要用于對服務器進行負載等測試,并分析和測量服務器的各種性能指標。Gatling主要用于測量基于HTTP的服務器,比如Web應用程序,RESTful服務等,除此之外它擁有以下特點: 支持Akka Actors 和 Async IO,從而能達到很高的性能 支持實時生成Html動態輕量報表,從而使報表更易閱讀和進行數據分析 支持DSL腳本,從而使測試腳本更易開發與維護 支持錄制并生成測試腳本,從而可以方便的生成測試腳本 支持導入HAR(Http Archive)并生成測試腳本 支持Maven,Eclipse,IntelliJ等,以便于開發 支持Jenkins,以便于進行持續集成 支持插件,從而可以擴展其功能,比如可以擴展對其他協議的支持 開源免費 Gatling適用的場景包括:測試需求經常改變,測試腳本需要經常維護;測試環境的客戶機性能不強,但又希望發揮硬件的極限性能;能對測試腳本進行很好的版本管理,并通過CI進行持續的性能測試;希望測試結果輕量易讀等。 相關廠商內容 QCon上海技術訓練營:OSGi、GitHub、Scrum深度培訓,10月29-31日與您相約,了解詳情! 2013年10月26日QClub大連站:大連軟件開發者大會 QCon上海2013“團隊文化”專題:構建持續前進的團隊文化、價值觀,工具與體系 GitHub中國上海Drinkup活動,就在QCon上海2013前夜,貝尼酒吧 QCon上海2013“游戲服務器實踐”專題:游戲服務器運維經驗、架構分享、性能策略 二,Gatling與JMeter JMeter是目前使用最為廣泛的服務器性能測試工具之一,它最大的特點就是擁有一套簡單易用的GUI,但它最大的缺點也是由于簡單易用導致它某些方面的不足,比如測試腳本(XML)不容易維護等。Gatling正是針對JMeter的劣勢做了大量改進,因此相較于 JMeter,Gatling擁有以下優勢: 在并發性能方面,Gatling使用了Akka Actors和Async IO, 而JMeter則采用了一個用戶使用一個線程的方式 ,一旦并發線程過多,性能就急速下降,很難充分發揮硬件的能力。雖然兩個工具都是基于JVM的,但是Actors模型的性能在高并發的情況下性能大大優于Threads,從而使得Gatling在更少的內存和CPU的情況下可以提供同樣的測試能力,降低了測試成本。圖1和圖2分別展現了二者在并發性能方面的表現。 圖1,JMeter 2.8 圖2,Gatling 1.3.2 圖片,測試環境和測試腳本參見:https://github.com/excilys/gatling/wiki/Benchmarks 其中圖1和圖2分別是JMeter和Gatling在300個用戶并發下的測試結果。可以很明顯的看出,JMeter的并發量在300上下波動,最高達到400,最低達到200,而Gatling幾乎穩定在300。由此可見Gatling性能的穩定性。 在測試腳本方面,Gatling是Scala代碼,而JMeter主要是XML代碼。Gatling基于一套開源的Gatling DSL API,所以它的功能很容易擴展,也不需要使用者精通Scala語言。DSL的使用也更容易編寫出簡明,易讀性和維護性高的代碼,而且還可以使用版本工具進行更有效的管理。因為性能測試應該屬于系統發布流程中必不可少的一個步驟,所以測試腳本應該和系統代碼一樣使用版本工具進行統一管理。 在報表系統上,Gatling提供了一套輕量并且十分友好的Html報表系統,使得用戶可以更為快速而方便地查看和分析數據,相反,JMeter的報表系統卻十分笨重,且使用也不方便。
三,如何在項目中使用Gatling 對于Gatling這樣一個全新的服務器性能測試工具,是否能將它很好的運用到項目中并發揮其優勢,這個是一個困擾測試決策者的問題。下面我將結合在一個真實項目中使用和部署Gatling的經驗來解答這個問題。 搭建測試環境 在一個大型的Web項目中,測試環境的搭建是項目測試工作開始的第一步,也是最為關鍵的一步,因為測試環境直接影響到測試成本和測試結果。由于這個項目對于性能的要求并不是很高,我們經過討論和分析,決定選取虛擬機作為測試平臺。這就意味著被測系統以及測試客戶端可以使用的硬件資源比如CPU和內存十分有限,因此需要測試工具能充分使用有限的資源發揮最大的性能。 進行負載測試 為了快速實現測試腳本,我首先選擇了使用Gatling錄制功能進行腳本錄制,成功錄制以后會在指定的“Output folder”目錄下面生成你指定“Class Name”為名字的腳本,見圖3。圖3,Gatling Recorder 根據圖3的配置,錄制好的腳本存放在/Users/twer/work/gatling/user-files/simulations/Common/MyRecordedSimulation.scala。生成的部分腳本代碼如下:class MyRecordedSimulation extends Simulation {val httpProtocol = http.baseURL("http:// :10.17.7.3").acceptHeader("image/png,image/*;q=0.8,*/*;q=0.5").acceptEncodingHeader("gzip, deflate").acceptLanguageHeader("en-US,en;q=0.5").connection("keep-alive").userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0)Gecko/20100101 Firefox/22.0")val headers_1 = Map("""Accept""" -> """text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8""","""If-None-Match""" -> """"a3ef335152d5532e2297bd8ad288f3f9"""")
錄制代碼段1.exec(http("request_21").get("""/customer/images/new?app_dialog=true&dialog=true""").headers(headers_18)).pause(6).exec(http("request_22").get("""/customer/images?_=1374400463122""").headers(headers_19)).pause(165 milliseconds).exec(http("request_23").get("""/Users/twer/work/gatling/user-files/simulations/testdata/test1.png""").headers(headers_20).check(status.is(500))).pause(2).exec(http("request_24").get("""/customer/images?view=list""")).pause(86 milliseconds)……setUp(scn.inject(atOnce(1 user))).protocols(httpProtocol)
錄制代碼段2 錄制出來的腳本擁有很多局限性: 只支持1個用戶 沒有檢測點 沒有邏輯分層 因此,它并不能用于真正的性能測試。對于這樣的原始代碼,我們需要進行大量的重構,使代碼擁有很好的可讀性和可維護性。 首先我們要進行分層處理: 對于錄制代碼段1,需要建立一個Header類來管理所有HTTP Header,這里使用“Headers.scala”,在錄制代碼段1中只給出了“headers_1”,實際的腳本包含了大量的Header。 對于錄制代碼段2,需要將測試場景和測試控制分開,每一個測試場景使用一個文件來保存,代碼段2所示的場景使用“UploadImageScenario.scala”來保存。主控腳本也需要分離出來存入“MainSimulation.scala”,通過調用不同的測試場景的腳本,從而可以復用HTTP的配置選項,比如: val httpProtocol = http .baseURL("http:// :10.17.7.3") 其次,我們還需要增加多用戶的支持: 多用戶數據的讀入,其中“user_credentials.csv”存儲的就是用戶名和密碼.feed(csv("user_credentials.csv")).exec(http("request_login").post("""/customer/login""").param("""username""", """${username}""").param("""password", "${password}""")
設置多用戶的值。由于我們使用的是虛擬機,所以經過測試,確定為400用戶并發。 setUp(LoginScenario.loginScn.inject(ramp(400 users) over(60 seconds))). protocols(httpProtocol) 最后,我們還要增加檢測點,使用check,find,status等函數進行檢測,下面的代碼檢測了用戶登出的時候HTTP Response Status是否為302:exec(http("request_logout").get(("""/customer/logout""").headers(headers_logout).check(status.is(302))) 當然,如果測試人員熟悉Gatling DSL API,我們也可以不用錄制代碼再進行重構,而是直接設計測試系統并進行測試案例的開發。 項目采取了敏捷方法進行開發,所以系統的一些功能在開發過程中會出現頻繁改動,導致測試場景和測試腳本也會隨之發生改變,因此,測試腳本的可讀性和可維護性對于我們來說就非常重要。當某個功能改變之后,使用Gatling腳本就能十分方便的進行閱讀和重構。比如對于添加user的功能,第一版只需要能添加user即可(見添加user代碼1),而在下一版中,則要求在添加user時可以選擇該user具有那些權限(見添加user代碼2),代碼如下:.exec(http("request_add_user").post("""/customer/users""").headers(headers_user).param("""utf8""", """?""").param("""user[username]""", """user2""").param("""user[email]""", """user@gmail.com""").param("""user[password]""", """user2""").param("""user[password_confirmation]""", """user2""")
添加user代碼1.exec(http("request_add_user").post("""/customer/users""").headers(headers_user).param("""utf8""", """?""").param("""user[username]""", """user2""").param("""user[email]""", """user@gmail.com""").param("""user[password]""", """user2""").param("""user[password_confirmation]""", """user2""").param("""user[plugins][]""", """customer_dashboard""").param("""user[plugins][]""", """customer_files""").param("""user[plugins][]""", """customer_images""").param("""user[plugins][]""", """customer_pages"""))
添加user代碼2 項目發布后,若項目功能發生改變,我們也可以使用Gatling進行持續的性能回歸測試,保證系統性能不會因為某次修改導致非預期的降低。如果降低了,就要進行及時的調查,修復或者是調整,保證性能一直在預期的可控范圍內。 測試報表 Gatling測試報表基于HTML,并且在測試過程中業已生成,所以打開速度很快。而且,當把鼠標移動到不同數據軸上時,都會有彈出框顯示這個點上詳細的測試數據信息。這種動態顯示數據的方式非常方便查看和分析數據。考慮到項目真實數據的不便,我將通過Gatling官方網站給出的示例報表進行說明。 Gatling的報表分為兩類:GLOBAL和DETAILS,其中GLOBAL主要是請求相關的統計數據,比如每秒請求數,請求成功與失敗數等;其中DETAILS主要是請求時間相關的統計數據,比如請求響應時間,請求響應延遲時間等。圖4 每秒請求數 當鼠標放到圖中任何一個點的時候,對應時間點上請求的詳細數據就會以圖中白色的彈出框的方式進行顯示。在下面的請求響應延遲時間圖里面也有同樣的功能。圖5 請求響應延遲時間 3,與CI的集成 項目的CI系統選用的是Jenkins,因為Jenkins有Gatling的插件,所以通過這個插件可以在Jenkins上直接查看Gatling的測試結果,如圖6所示。圖6 Jenkins Gatling插件 我們還把生成的報表存檔到每個Build里面,這樣就可以在每個Build中獲得那次測試的所有報表。 更多類型的測試 其他類型的HTTP服務器性能測試,比如瞬間壓力測試,耐久性測試等,都十分適合使用Gatling。 四,未來的Gatling Gatling發布的時間雖然不長,但憑借其優良的性能,DSL模式的腳本,輕量友好的報表系統在眾多服務器性能測試工具中脫穎而出。在2013年5月發布的ThoughtWorks技術雷達中,Gatling被列入了ADOPT,并在一些ThoughtWorks項目中得到了實際的運用。不過,Gatling還是存在一些問題,比如不支持分布式模型;默認只支持HTTP,對于其他協議需要自己動手進行擴展;報表種類也不是很豐富 。倘若Gatling 能在這幾方面有所突破,那么它必將成為新一代服務器性能測試工具中的殺手锏。
三,如何在項目中使用Gatling
對于Gatling這樣一個全新的服務器性能測試工具,是否能將它很好的運用到項目中并發揮其優勢,這個是一個困擾測試決策者的問題。下面我將結合在一個真實項目中使用和部署Gatling的經驗來解答這個問題。
搭建測試環境
在一個大型的Web項目中,測試環境的搭建是項目測試工作開始的第一步,也是最為關鍵的一步,因為測試環境直接影響到測試成本和測試結果。由于這個項目對于性能的要求并不是很高,我們經過討論和分析,決定選取虛擬機作為測試平臺。這就意味著被測系統以及測試客戶端可以使用的硬件資源比如CPU和內存十分有限,因此需要測試工具能充分使用有限的資源發揮最大的性能。
進行負載測試
為了快速實現測試腳本,我首先選擇了使用Gatling錄制功能進行腳本錄制,成功錄制以后會在指定的“Output folder”目錄下面生成你指定“Class Name”為名字的腳本,見圖3。
圖3,Gatling Recorder
根據圖3的配置,錄制好的腳本存放在/Users/twer/work/gatling/user-files/simulations/Common/MyRecordedSimulation.scala。生成的部分腳本代碼如下:
class MyRecordedSimulation extends Simulation { val httpProtocol = http .baseURL("http:// :10.17.7.3") .acceptHeader("image/png,image/*;q=0.8,*/*;q=0.5") .acceptEncodingHeader("gzip, deflate") .acceptLanguageHeader("en-US,en;q=0.5") .connection("keep-alive") .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0") val headers_1 = Map( """Accept""" -> """text/html,application/xhtml+xml,application/xml;q=0.9, */*;q=0.8""", """If-None-Match""" -> """"a3ef335152d5532e2297bd8ad288f3f9"""") |
錄制代碼段1
.exec(http("request_21") .get("""/customer/images/new?app_dialog=true&dialog=true""") .headers(headers_18)) .pause(6) .exec(http("request_22") .get("""/customer/images?_=1374400463122""") .headers(headers_19)) .pause(165 milliseconds) .exec(http("request_23") .get("""/Users/twer/work/gatling/user-files/simulations/testdata/test1. png""") .headers(headers_20) .check(status.is(500))) .pause(2) .exec(http("request_24") .get("""/customer/images?view=list""")) .pause(86 milliseconds) …… setUp(scn.inject(atOnce(1 user))).protocols(httpProtocol) |
錄制代碼段2
錄制出來的腳本擁有很多局限性:
只支持1個用戶
沒有檢測點
沒有邏輯分層
因此,它并不能用于真正的性能測試。對于這樣的原始代碼,我們需要進行大量的重構,使代碼擁有很好的可讀性和可維護性。
首先我們要進行分層處理:
對于錄制代碼段1,需要建立一個Header類來管理所有HTTP Header,這里使用“Headers.scala”,在錄制代碼段1中只給出了“headers_1”,實際的腳本包含了大量的Header。
對于錄制代碼段2,需要將測試場景和測試控制分開,每一個測試場景使用一個文件來保存,代碼段2所示的場景使用“UploadImageScenario.scala”來保存。主控腳本也需要分離出來存入“MainSimulation.scala”,通過調用不同的測試場景的腳本,從而可以復用HTTP的配置選項,比如:
val httpProtocol = http
.baseURL("http:// :10.17.7.3")
其次,我們還需要增加多用戶的支持:
多用戶數據的讀入,其中“user_credentials.csv”存儲的就是用戶名和密碼
.feed(csv("user_credentials.csv")) .exec(http("request_login") .post("""/customer/login""") .param("""username""", """${username}""") .param("""password", "${password}""") |
設置多用戶的值。由于我們使用的是虛擬機,所以經過測試,確定為400用戶并發。
setUp(LoginScenario.loginScn.inject(ramp(400 users) over(60 seconds))).
protocols(httpProtocol)
最后,我們還要增加檢測點,使用check,find,status等函數進行檢測,下面的代碼檢測了用戶登出的時候HTTP Response Status是否為302:
exec(http("request_logout")
.get(("""/customer/logout""")
.headers(headers_logout)
.check(status.is(302)))
當然,如果測試人員熟悉Gatling DSL API,我們也可以不用錄制代碼再進行重構,而是直接設計測試系統并進行測試案例的開發。
項目采取了敏捷方法進行開發,所以系統的一些功能在開發過程中會出現頻繁改動,導致測試場景和測試腳本也會隨之發生改變,因此,測試腳本的可讀性和可維護性對于我們來說就非常重要。當某個功能改變之后,使用Gatling腳本就能十分方便的進行閱讀和重構。比如對于添加user的功能,第一版只需要能添加user即可(見添加user代碼1),而在下一版中,則要求在添加user時可以選擇該user具有那些權限(見添加user代碼2),代碼如下:
.exec(http("request_add_user") .post("""/customer/users""") .headers(headers_user) .param("""utf8""", """?""") .param("""user[username]""", """user2""") .param("""user[email]""", """user@gmail.com""") .param("""user[password]""", """user2""") .param("""user[password_confirmation]""", """user2""") |
添加user代碼1
.exec(http("request_add_user") .post("""/customer/users""") .headers(headers_user) .param("""utf8""", """?""") .param("""user[username]""", """user2""") .param("""user[email]""", """user@gmail.com""") .param("""user[password]""", """user2""") .param("""user[password_confirmation]""", """user2""") .param("""user[plugins][]""", """customer_dashboard""") .param("""user[plugins][]""", """customer_files""") .param("""user[plugins][]""", """customer_images""") .param("""user[plugins][]""", """customer_pages""")) |
添加user代碼2
項目發布后,若項目功能發生改變,我們也可以使用Gatling進行持續的性能回歸測試,保證系統性能不會因為某次修改導致非預期的降低。如果降低了,就要進行及時的調查,修復或者是調整,保證性能一直在預期的可控范圍內。
測試報表
Gatling測試報表基于HTML,并且在測試過程中業已生成,所以打開速度很快。而且,當把鼠標移動到不同數據軸上時,都會有彈出框顯示這個點上詳細的測試數據信息。這種動態顯示數據的方式非常方便查看和分析數據。考慮到項目真實數據的不便,我將通過Gatling官方網站給出的示例報表進行說明。
Gatling的報表分為兩類:GLOBAL和DETAILS,其中GLOBAL主要是請求相關的統計數據,比如每秒請求數,請求成功與失敗數等;其中DETAILS主要是請求時間相關的統計數據,比如請求響應時間,請求響應延遲時間等。
圖4 每秒請求數
當鼠標放到圖中任何一個點的時候,對應時間點上請求的詳細數據就會以圖中白色的彈出框的方式進行顯示。在下面的請求響應延遲時間圖里面也有同樣的功能。
圖5 請求響應延遲時間
3,與CI的集成
項目的CI系統選用的是Jenkins,因為Jenkins有Gatling的插件,所以通過這個插件可以在Jenkins上直接查看Gatling的測試結果,如圖6所示。
圖6 Jenkins Gatling插件
我們還把生成的報表存檔到每個Build里面,這樣就可以在每個Build中獲得那次測試的所有報表。
更多類型的測試
其他類型的HTTP服務器性能測試,比如瞬間壓力測試,耐久性測試等,都十分適合使用Gatling。
四,未來的Gatling
Gatling發布的時間雖然不長,但憑借其優良的性能,DSL模式的腳本,輕量友好的報表系統在眾多服務器性能測試工具中脫穎而出。在2013年5月發布的ThoughtWorks技術雷達中,Gatling被列入了ADOPT,并在一些ThoughtWorks項目中得到了實際的運用。不過,Gatling還是存在一些問題,比如不支持分布式模型;默認只支持HTTP,對于其他協議需要自己動手進行擴展;報表種類也不是很豐富 。倘若Gatling 能在這幾方面有所突破,那么它必將成為新一代服務器性能測試工具中的殺手锏。
posted on 2013-10-24 10:52 順其自然EVO 閱讀(342) 評論(0) 編輯 收藏 所屬分類: 性能測試