如何調整壓力測試工具?

  如何調整壓力測試工具

  您是否曾經不得不對應用程式進行壓力測試,而最後卻發現不明白結果表明什麼意義?也許問題不是出在應用程式上。也許問題出在配置壓力測試工具的方式上。如果您曾經經歷過這種情況,或者正要進行壓力測試,您就需要考慮以下幾個方面。

  如何進行測試?

  我經常遇到一些開發團隊,他們收到諸如“客戶端將每小時處理20個客戶”此類的效能需求。團隊就試圖把該需求轉化為某種測試。執行這種測試的常見方法就是以死迴圈的形式對伺服器進行反覆請求,然後靜觀其效。通常事情進行得不是很順利,這就是為什麼隨後我會作為一個性能專業化方面的顧問“遇見他們”的原因。通常我問的第一個問題是:“您是如何進行測試的?”一般來說,答案會是:“我們將請求置於迴圈中,然後計算伺服器可以處理的請求的數目。”正是這種回答使我明白首先要做的就是調整測試工具本身。

  如果您不明白上述測試有什麼問題,不要擔心——有很多人和您一樣。進行一次切實可行的壓力測試並不像乍看之下那麼簡單。遇到的問題可能非常微妙,而且通常只有採用不那麼簡單的方法來理清情況才能看清問題。但是這並不是讓您目光呆滯地深入研究馬爾可夫鏈(Markov
chains)、狀態改變模型、排隊理論、概率分佈等等,讓我們以一種不那麼乏味的、更通俗易懂的方式來說明如何解決這個在許多壓力測試中出現的常見問題。

  測試方式將影響測試

  我們首先要明白的是,雖然測試通常都是從客戶端活動的角度定義的,但是它們必須從以伺服器為中心的視角來看待。以伺服器為視角將只看到客戶端訪問的頻率和處理每個請求所花費的時間。讓我們考慮一個典型例子,即銀行的出納員。出納員通常不知道您是什麼時候到的,也不知道您是從哪裡來的。他們所知道的只是您在這裡,而且您要讓他們為您做一些事情。現在,佇列中有多少人將取決於人們到達的速度,以及滿足他們的要求所花的時間。

  比佇列中有多少人更重要的是,隨著後來的人不斷補進佇列,房間中的人數是在減少、保持不變還是在增加?與之相隨的另一個問題是,人們進入佇列的速度與離開的速度相比,是快一些、相同還是慢一些?如果離開的速度要比到達的速度快,那麼處理請求的速度要比遞交請求的速度快。第二種情況說明剛剛處理完一個客戶,下一個就到達了。最後一種情況則說明人們到達的速度要比處理的速度快。用數學術語來說,第一種系統是收斂的,第二種處於穩定狀態,第三種則是發散的。這三種情況中房間中的人數都是由利托氏定理(Little's
Law)決定的。

  只做力所能及的

  對於外行來說,利托氏定理說明了您只能做這麼多工作。其數學版本是這麼說的:系統中的請求數等於請求到達的速度乘以它們在系統中的時間所產生的積。如果它們在系統中的時間取決於流出系統的速度(通常稱為服務時間),那麼就可以通過觀察請求到達的頻率(請求到達間隔時間)並與服務時間比較,而確定系統處於哪種狀態。

  對於每種情況,利托氏定理都描述了系統是如何處理工作負載的。雖然狀態可能會發生瞬時的迸發和間歇,總體的趨勢還將由平均的狀況決定。例如,在收斂系統中,可能會由於許多人同時進入佇列而產生瞬時的暴漲,但是佇列仍將會騰空,因為收斂系統的傾向就是趨向空閒。但是,第三種場景是發散的,其中的請求數將會無限增長。它會嗎?這個問題的答案與如何定義發出請求的全域有關。

  在某個隨機的時間點,全域中的使用者將發出一個請求。這肯定是從以伺服器為中心的視角來看全域了。大多數系統都基於一個假設,即在任一個給定的時間點,全域中只有一部分會發出請求。經驗告訴我們,在許多因特網應用程式中,全域中有10%在任意時間點都是活動的。我們需要知道這種資訊,如果我們要定義實際的壓力測試的話。例如,如果全域中有1000個使用者,我們會預料有100個每時每刻都在使用系統。由於我們估計會有10%的併發使用,使用者庫又有1000個使用者,所有我們的測試應該模擬100個使用者重複執行一些請求系列。用這種方法定義測試的危害是它反映的是客戶端的視角。

  當我們從以伺服器為中心的視角轉向以客戶端為中心的視角後,就看不到向伺服器傳送請求的速度了。如果我們限制或固定為執行使用者請求所分配的使用者(執行緒)數目,那麼就看得更模糊了。在這種情況下進行測試,我們將看到伺服器正在處理穩定的請求流,而處理請求的時間似乎越來越長。

  每個人都可以參與

  如果我們讓模擬執行緒儘可能快地發出請求,就是在模擬整個全域(甚至更多)的使用者都在同一時間發出請求。我們假定伺服器模型為單一的,因為這樣便於理解;多伺服器模型的工作方式是相同的,只是更快一些。系統將把請求排隊,並且每次只處理一個。一旦有一個請求清除,執行緒會立即返回佇列頭部發出下一個請求。雖然這種事件順序似乎說明我們處理的是一個穩定狀態的系統,但我們實際上是在處理髮散系統。它之所以看起來像穩定狀態系統的惟一原因是,我們限制了發出請求的執行緒數目。正如前面所提到的,在發散系統中,每個後繼使用者的響應時間都要比前一個所經歷的時間長。這意味著平均響應時間將不斷地增長而沒有限制。儘管如此,但是我們人為地限制了客戶端的數目,因此平均響應時間將穩定在一個點上,該點取決於客戶端數目與處理單個請求所花費時間的乘積。這裡所說的這種系統中的響應時間包括花在佇列中的時間,而且因為花在佇列中的時間比預料的要少,所以我們又人為擴大了測量值。最終結果是您的測試限制了您確定系統的可伸縮性的能力。

  如何修復

  要修復壓力測試,需要知道使用者/執行緒發出請求的速度。所有使用者的速度之和就轉化為伺服器接受請求的速度。一旦確定了這個值,就可以對工具發出請求的速度進行調整。下面的表列出了幾個可以用來維持50個請求每秒(RPS)的值。從伺服器的視角來看,工具需要每20ms提供一個請求。這種觀點反映的是單個執行緒的情況。如果工具配置了兩個執行緒,那麼對於每個執行緒,都應該維持40ms的請求間時間間隔。表中還列出了使用5個執行緒和10個執行緒的情況下的時間間隔。

  抉擇

  從理論上來說,這個表展示瞭如何使用1個、2個、5個、10個執行緒來實現所要求的維持50個RPS的目標。但是如果服務時間比請求間時間間隔長的話會怎麼樣呢?這種情況下駐留在伺服器中的執行緒不能使下一個請求排入佇列,工具也不能交付50RPS的預期負載。為了避免這種情況發生,需要在系統中構建一些空餘時間(slack)。使用大量執行緒的方案對我們來說通常是不可行的,因為我們很有可能要受到可用的硬體數目和/或許可證數目(對於商業的負載測試工具來說)的限制。解決方法其實很常見,就是我們需要達到一種維持合適的請求間時間間隔與使用過多的(計算/許可)資源之間的平衡。我們要始終記住,如果測試工具使用的資源(不管是硬體、軟體或是執行緒)很少,就會影響我們測試的有效性。

  三思而後行

  我們使用Apache
JMeter來對隨機的Web應用程式進行負載測試,以說明壓力測試工具是如何影響測試結果的。除了要知道應用程式的入口點是Servlet,應用程式的功能以及如何實現的詳細資訊對於我們的討論來說並不重要。

  圖1顯示了在平均響應時間下增加執行緒數目的效果。其中粉紅色的線是沒有調整執行緒的情況。藍色的線是在每兩個執行緒之間添加了500ms的空餘時間後的執行緒。從圖中可以看出,兩種情況的結果差別非常小。每種情況都清楚表明,隨著系統的負載增加,響應時間也會增加。既然我們已經知道伺服器的效能會隨總體負載的增加而降低,這樣的結果也就不奇怪了。我們只是在看圖2所示的結果時才能看出存在的問題。

  圖1

  圖2

顯示維持穩定的請求速度的能力最初是受制於執行緒數目的。同樣這也不能說明問題,因為有理由假定,在超出特定的執行緒數目閾值之前,不能維持合理的伺服器負載。圖2也顯示,一旦超出了伺服器處理請求的能力,執行緒的增加對工具向伺服器發出請求的整體速度的影響就不明顯了。另外一點是,這些“額外”的執行緒所造成的響應時間的增加確實暗示它們影響了系統的負載。

  問題在於:為什麼不增加伺服器負載的執行緒看起來會降低伺服器的效能?一個可能的答案就是,並不是執行緒降低了伺服器的效能,而是伺服器一結束對執行緒的服務,執行緒就被排隊了。因為測量響應時間的計時器必須在將請求傳送給伺服器時啟動,在收到響應時停止,所以響應時間必然包括執行緒在佇列中等待服務的所有時間,再加上服務時間。因為執行緒一離開就進入系統,就造成了這樣的情況:執行緒必須等待其他每個執行緒完成後才能被服務。在這種場景下,執行緒越多就會造成佇列和響應時間越長。

  利托氏定理告訴我們,這種系統是發散的,而由此可以得出結論:工具妨礙了確定真正的瓶頸(如果存在的話)的能力。

  放慢速度,做得更多

  利托氏定理包括兩個部分:服務時間和頻率。如果我們以工具的眼光來看世界,那麼我們會發現我們不能控制服務時間。但是我們確實能控制頻率。既然前面的工作說明我們進行得太快了(或者說在錯誤的方向上進行得太快了),而我們惟一能控制的就是頻率,那麼我們惟一能做的就是放慢速度。我們可以通過在每兩個請求之間插入間歇來達到這個目的。這將會降低單個執行緒啟動請求的速度。間歇會降低執行緒在佇列中的時間,從而提供更符合現實的響應時間。

  為了測試,我們將啟動50個經過調整的每秒產生9個請求的執行緒。如果我們發現不能維持合理的請求速度,這些值還可以調整。使用響應時間來評價效果。最後要設定的是間歇時間。可以使用由先前執行得出的資料來幫助我們做出決定。

  回到圖1,我們可以看到,8到9個RPS會產生2到3秒的響應時間。利托氏定理告訴我們,我們需要足夠的執行緒,以便在2到3秒的時間幀後就可以自由進入系統(假定可以提高平均響應時間)。因此平均的間歇時間大約是3秒。為了練習,我們將執行一系列的測試來探討值的範圍。

  第一次測試使用2到2.5秒之間的一個隨機選取的值。這個範圍的值的平均間歇時間是3.5秒。可以利用這條資訊計算請求的理論速度:用50(執行緒數目)除以3.5+2(目標響應時間的估測值)。得到的值是9.1RPS。第二次測試使用3到6秒之間的一個隨機值。最終測試使用4到6之間的值。這些測試的結果如圖3所示。

  圖3

  圖3說明增加間歇的時間會使平均響應時間縮短。但是這條資訊需要與圖4所傳達的資訊相結合。在圖4中,我們可以看到,當間歇時間增加到4-7秒時不能保持要求的向伺服器發出請求的速度。我們可以通過新增更多的執行緒來增加負載,但是這一步中存在最小值,因為這些測試的確為我們提供了有效的配置。

  圖4

  這一系列的測試有助於將壓力測試推進到一個更好的配置。我們的結論是:應該配置我們的測試工具,使其使用50個執行緒,每個執行緒的間歇時間為3到6秒。

  結束語

  在開始效能調優實踐(或者為效能確定基準)之前,需要確認工具不會影響測試。配置良好的工具不會讓我們測量不該測量的資料。不能交付適當的負載或會使我們測量偶然的響應時間的測試工具將會影響對應用程式進行效能調優的工作。要想知道是否發生了這種情況,關鍵是要度量工具以正常速度執行的效果。這種效果可以由工具滿足或支援所要求的每秒的事務或請求數目的能力來確定。工具不應該立刻輪換執行緒(來發出下一個請求)。如果發生了這種情況,就需要降低工具的速度,以免人為地使伺服器的容量溢位。通常需要試驗幾次以達到測試工具的適當的平衡配置。在測試的早期階段,不要把重點放在響應時間(它會隨著對應用程式調優的過程而改善)上,而是要放在配置好工具上。最後,不要害怕放慢速度,因為這樣做可能有助於弄清楚到底是什麼影響了應用程式的效能。

相關問題答案