1. 程式人生 > >我們如何使用HAProxy實現單機200萬SSL連線

我們如何使用HAProxy實現單機200萬SSL連線

導讀:架構師需要精確的瞭解服務的支撐能力,也希望通過調優來發揮單個節點最大的價值。本文分享了壓測及調優 HAProxy 實現 200 萬併發 SSL 連線的過程,由高可用架構翻譯,轉載請註明出處。

高可用架構

先觀察上面截圖,可以看到兩個關鍵資訊:

  • 這臺機器已經建立了 238 萬個 TCP 連線
  • 使用記憶體大約在 48G。

下面將會介紹在單個 HAProxy 機器上實現這種規模訪問所需的配置。本文是負載測試 HAProxy 系列文章的最後一篇。有時間的讀者建議閱讀本系列的前兩篇(見文末連結),它將幫助您瞭解相應的核心調優方法。

在這個配置過程中,我們也使用了很多小元件幫助我們達到目標。

在展開最終 HAProxy 配置之前,我想給大家回顧一下負載測試的歷程及想法,心急的讀者可以直接跳到文章後段查閱相關 HAProxy 配置。

測試目標

我們要測試的元件是 HAProxy 1.6 版。生產環境是在 4 核 30 G 的機器上執行該軟體,當前所有的連線都是非 SSL 的。

測試目標有兩方面:

  1. 當將整個負載從非 SSL 連線轉移到 SSL 連線時,CPU 使用率增加的百分比。CPU 的使用率肯定會增加,這是由於 5 次握手的加長和資料包加密的開銷所帶來。
  2. 其次,希望能夠測試單個 HAProxy 每秒請求數和最大併發連線數的上限

目標一主要因為業務方面功能需要通過 SSL 進行通訊。 目標二是為了可以在生產環境中部署最少規模的 HAProxy 機器。

元件和配置

  • 使用多臺客戶端機器來執行 HAProxy 壓力測試。
  • 有各種配置的 HAProxy 1.6 的機器
    • 4核,30G
    • 16核,30G
    • 16核,64G
  • 相關後端伺服器,用於支援所有併發訪問。

HTTP 和 MQTT

我們的整個基礎設施支援兩種協議:

  • HTTP
  • MQTT

在我們的技術棧中,沒有使用 HTTP 2.0,因此在 HTTP 上沒有長連的功能。所以在生產環境中,單個 HAProxy 機器(上行 + 下行)的最大數量的 TCP 連線在(2 * 150k)左右。雖然併發連線數量相當低,但每秒請求的數量卻相當高。

另一方面,MQTT 是一種不同的通訊方式。它提供高質量的服務引數和持久的連線性。因此,可以在 MQTT 通道上使用雙向長連通訊。對於支援 MQTT(底層 TCP)連線的 HAProxy,在高峰時段會看到每臺機器上大約有 600 – 700k 個 TCP 連線。

我們希望進行負載測試,這將為我們提供基於 HTTP 和 MQTT 連線的精確結果。

有很多工具可以幫助我們輕鬆地測試 HTTP 伺服器,並且提供了高階功能,如結果彙總,將文字轉換為圖形等。然而,針對 MQTT,我們找不到任何壓力測試工具。我們確實有一個自己開發的工具,但是它不夠穩定,不足以支援這種負載。

所以我們決定使用客戶端測試 HTTP 負載,並在 MQTT 伺服器使用相同配置。

初始化設定

考慮到相關內容對於進行類似的壓力測試或調優的人來說有幫助,本文提供了很多相關細節,篇幅稍微有些長。

  • 我們採用了一臺 16 核 30G 機器來執行 HAProxy,考慮到 HAProxy 的 SSL 產生的 CPU 巨大開銷,因此沒有直接使用目前生產環境。
  • 對於伺服器端,我們使用了一個簡單的 NodeJs 伺服器,它在接收到 ping 請求時用 pong 進行回覆。
  • 對於客戶端,我們最終使用 Apache Bench。使用 ab 的原因是因為它是一個大家熟悉和穩定的負載測試工具,它也提供了很好的測試結果彙總,這正是我們所需要的。

ab 工具提供了許多有用的引數用於我們的負載測試,如:

  • -c,指定訪問伺服器的併發請求數。
  • -n,顧名思義,指定當前負載執行的請求總數。
  • -p,包含 POST 請求的正文(要測試的內容)。

如果仔細觀察這些引數,您會發現通過調整所有這三個引數可以進行很多排列組合。示例 ab 請求將看起來像這樣

ab -S -p post_smaller.txt -T application/json -q -n 100000 -c 3000

http://test.haproxy.in:80/ping

這樣的請求的示例結果看起來像這樣

我們感興趣的數字是:

  • 99% 的返回請求的響應延遲時間。
  • Time per request:每個請求的時間
  • No. of failed requests:失敗請求數。
  • Requests per second: 每秒請求量

ab 的最大問題是它不提供控制每秒發起請求量,因此我們不得不調整 -c 併發級別以獲得所需的每秒鐘請求數,並導致很多後文提到的問題和錯誤。

測試圖表

我們不能隨機地進行多次測試來獲得結果,這不會給我們提供任何有意義的資訊。我們必須以某種具體的方式執行這些測試,以便從中獲得有意義的結果。來看看這個圖。

測試圖表

該圖表明,在某一點之前,如果不斷增加請求數量,延遲將幾乎保持不變。然而,達到某個臨界點,延遲將開始呈指數級增長。這就是該機器的臨界點。

Ganglia

在提供一些測試結果之前,我想提一下 Ganglia。

Ganglia 是用於高效能運算系統(如叢集和網格)的可擴充套件分散式監控系統。

看看截圖,瞭解 Ganglia 是什麼,以及它提供的關於底層機器的資訊。

Ganglia

Ganglia

通過 Ganglia 可以監測 HAProxy 機器上一些重要引數。

  1. TCP  established 這告訴我們在系統上建立的 TCP 連線總數。注意:這是上行和下行連線的總和。
  2. packets sent and received 傳送和接收的 TCP 資料包的總數。
  3. bytes sent and received 這將顯示傳送和接收的位元組數。
  4. memory 隨著時間的推移使用的記憶體數。
  5. network 通過線路傳送資料包而消耗的網路頻寬。

以下是通過通過負載測試找到的已知限制。

700k TCP 連線,

50k 傳送包數量,60k 接收包數量,

10-15MB 傳送及接收的位元組數,

14-15G 記憶體峰值,

7MB 頻寬。

所有這些值都是基於每秒資料

HAProxy Nbproc

最初,當我們開始測試 HAProxy 時,發現使用 SSL 情況下,CPU 很早就到了瓶頸,而每秒請求數都很低。 在使用 top 命令後,發現 HAProxy 只使用 1 個 CPU 核。 而我們還有 15 個以上的核沒用。
Google 了 10 分鐘後,我們在 HAProxy 中找到某個設定,可以讓 HAProxy 使用多個核。

它被稱為 nbproc,具體設定請看這篇文章 [8]:
調整此設定是我們的負載測試策略的基礎。  讓我們可以方面的進行 HAProxy 組合以便測試。

使用 AB 進行壓力測試

當開始負載測試之旅時,我們不清楚應該測量的指標和需要達到的目標。

最初,我們只有一個目標:通過改變所有下面提到的引數來找到臨界點

我保留了各種負載測試結果的表格。 總而言之,做了 500 多次測試,以達到最終的效果。 您可以清楚地看到,每次測試都有很多不同的部分。

單客戶端問題

我們看到客戶端正在成為瓶頸,因為我們不斷增加每秒的請求數。 ab 使用單個核,從文件中可以看出,它不提供使用多核的功能。
為了有效地執行多個客戶端,我們發現一個有用的 Linux 工具叫做 Parallel [7]。 顧名思義,它可以幫助您同時執行多個命令來達到並行的目的。 正是我們想要的。
看一下使用 Parallel 執行多個客戶端的示例命令。

 Parallel

上述命令將執行 3 個 ab 客戶端擊訪問同一個 URL。 這有助於我們消除客戶端瓶頸。

Sleep 及 Times 引數的問題

下面是 Ganglia 中的一些引數。讓我們簡單討論一下。

  1. packets sent and received 為了產生更多資料,可以在 post 請求中新增更多資料
  2. tcp_established 這是想實現的目標。想象一下,如果單個 ping 請求大約需要一秒鐘,那麼每秒需要大約 700k 個請求來達到 tcp_established 的目標。現在這個數字在生產環境中可能看起來更容易達到,但是在測試場景中不太可能達到。

我們在 POST 呼叫中加入了一個 sleep 引數,它指定了服務端傳送返回之前需要 sleep 的毫秒數。這將模擬長時間執行的生產環境請求。如果讓請求 sleep 20 分鐘的話,只需要每秒發出 583 個請求就能達到 700k 併發連線的標準。

此外,我們還在 POST 呼叫中引入了另一個引數: times。伺服器在返回請求時應該在 TCP 連線上寫入響應的指定次數,這有助於模擬更多的資料。

Apache Bench (AB) 的問題

雖然使用 AB 也得到了不少測試結果,但同時也遇到了很多問題。我不會在這裡提到所有問題,因為不是這篇文章重點(下面介紹另一個客戶端)。

我們非常滿意從 ab 上獲得的結果,但是它不支援在一段時間內生成所需指定的 TCP 連線數。不知何故,我們設定的 sleep 引數在 ab 上無法生效。

雖然在一臺機器上可以執行多個 ab 客戶端並且可以用工具合併結果,但是在多臺客戶機上執行此設定對我們來說仍然是一件痛苦的事情。那時我還沒有聽說過 pdsh [4] 這個工具。

此外,我們也沒有關注過超時的問題。在 HAProxy,ab 客戶端和伺服器上有一些預設的超時設定,我們完全忽略了這些。後文會講到。

我們一開始就提到通過臨界點圖來檢測系統的上限,但講了這麼多有點偏離了最主要目標。然而,要得到有意義的結果只能著眼於這一點。

使用 AB 碰到的一個問題是到了某個點 TCP 連線數不再增加。我們有大約 40 – 45 個客戶端執行在 5 – 6 臺客戶端機上,但依然不能達到想要的規模。理論上,TCP 連線的數量應該隨著 sleep 時間的增加而增加,但對我們來說並非如此。

引入 Vegeta

因此我們需要尋找一個負載測試工具,這些工具需要具有更好的擴充套件性和更好的功能性,最終,我們找到了 Vegeta [6]。
從我的個人經驗來看,我已經看到 Vegeta 具有極高的擴充套件性,與 ab 相比,它提供了更好的功能。 在我們的負載測試中,單個 Vegeta 客戶端能夠產生相當於 15 倍 ab 的吞吐量。

下面,我將提供使用 Vegeta 的負載測試結果。

使用 Vegeta 進行負載測試

首先,看看我們用來執行一個 Vegeta 客戶端的命令。 進行測試的命令稱為 attack:(酷吧?)

我們太喜歡 Vegeta 提供的引數了,來看看下面的一些引數。

  • -cpus = 32 指定此客戶機要使用的 CPU 核數。 由於要生成的負載量,我們不得不將客戶機擴充套件到 32 核 64G。 雖然上面的速度也不是特別高。 但是當有大量處於 sleep 狀態的連線時,維持這些連線也會產生比較大的開銷。
  • -duration = 10m 我想這是不言自明的。如果沒有指定任何持續時間,測試將永遠執行。
  • -rate = 2000 每秒請求的數量。

所以如上圖所示,我們在一臺 4 核機器上每秒達到了 32k 請求量。 如果你記得臨界點圖,在這種情況下,非 SSL 請求的臨時點是 31.5k。
從負載測試中看更多的結果。

16k 的 SSL 連線也不錯。 請注意,在我們的負載測試過程中,必須從頭開始,因為我們採用了一個新的客戶端,它給了我們比 ab 更好的結果。 所以不得不再來一遍。

CPU 核數的增加導致機器在未達到 CPU 限制前,每秒可以用的請求數增加。

如果將 CPU 核數從 8 個增加到 16 個,我們發現每秒的請求數量並沒有大幅度增加。如果在生產環境中使用 8 核機器,那麼我們不會分配所有的核給 HAProxy,或者是它的任何其他程序。 所以我們決定用 6 核機器進行一些測試,看看是否能得到可接受的數字。

結果還不錯。

引入 sleep

我們現在對負載測試結果非常滿意。 然而,這並沒有模擬真正的生產場景。 當我們引入 sleep,才開始模擬生產環境的情況。

echo “POST https://test.haproxy.in:443/ping” | vegeta -cpus=32 attack -duration=10m  -header=”sleep:1000″  -body=post_smaller.txt-rate=2000 -workers=500  | tee reports.bin | vegeta report

因此,x 毫秒的隨機 sleep 時間將導致伺服器 sleep 時間為 0 < x < 1000 。 因此上述負載測試將給出平均 ≥ 500ms 的延遲。

最後一個單元格中的含義是 TCP established, Packets Rec, Packets Sent

從表中可以看到,6 核機器可以支援的最大請求量從 20k 減少到 8k。 顯然,sleep 有其影響,影響的是 TCP 連線的數量。 然而這距離我們設定的 700K 目標還很遠。

里程碑 #1

我們如何增加 TCP 連線的數量? 很簡單,不斷增大 sleep 時間,連線數應該上升。 我們一直增加 sleep 時間並在 60 秒的 sleep 時間停了下來。 這意味著大約 30 秒的平均延遲。

Vegeta 可以提供成功請求百分比的結果引數。 我們看到,在上述的 sleep 時間,只有 50% 的呼叫是成功的。 請看下面的結果。

我們達到了 400 萬個 TCP 連線,在每秒 8k 請求和 60s 的 sleep 時間的情況下。 60000R 的 R 表示隨機。

我們的第一個的發現是,在 Vegeta 中有一個預設的超時時間是 30 秒,這就解釋了為什麼 50% 的請求會失敗。 所以我們在後續測試中將超時調整到 70 秒,並隨著需求的變化而不斷變化。

 Vegeta

在客戶端調整超時值之後,我們可以輕鬆地達到 700k 標準。 唯一的問題是這些不可持續,只是峰值的資料。 系統達到了 600k 或 700k 的峰值連結,但並沒有堅持很長時間。

但是我們想要得到圖上連線持續很久的效果

這顯示了穩定保持 780k 連線的狀態。如果仔細檢視上面的統計資訊,每秒的請求數量非常多。然而,在生產環境中,我們在單個  HAProxy 機器上的請求數量要少得多(約 300 個)。

我們確信,如果減少生產環境的 HAProxy 的數量(約 30 個,這意味著每秒 30 * 300〜9k 的連線),我們將會達到機器 TCP 連線限制,而不是 CPU。

所以我們決定實現每秒 900 個請求、30MB/s 的網路流量,以及 210 萬 TCP 連線。我們選用這些數字,因為這將是單個生產環境 HAProxy 機器的 3 倍流量。

到目前為止,我們已經配置了 HAProxy 使用 6 核。我們只想測試 3 核,因為這是我們在我們的生產機器上使用的最簡單的方法(如前所述,我們的生產機器是 4 核 30G,所以用 nbproc = 3 進行更改將是最簡單的)。

里程碑 #2

現在我們對每秒請求的最大限制可以隨機器不同而變化,所以我們只剩下一個任務,如上所述,實現 3 倍的生產負載:

  • 每秒 900 個請求
  • 建立了 210 萬個 TCP 連結。
  • 30 MB/s 網路。

在 220k 的測試環境下,我們再次陷入僵局。 無論客戶機數量多少或睡眠時間多少,TCP 連線數似乎都停留在那裡。

我們來看一些估算資料。 220k TCP 連線,每秒 900 個請求 = 110,000 / 900〜= 120 秒。達到了 110k,因為 220k 連線包括上行和下行。

當我們在 HAProxy 開啟日誌時,我們懷疑 2 分鐘是系統某處的限制。 我們可以看到 120,000 ms 是日誌中大量連線的總時間。

在進一步調查中,我們發現 NodeJs 的預設請求超時為 2 分鐘。 瞧!

但我們的高興顯然很短暫,在 130 萬,HAProxy 連線數突然下降到 0,並再次開始增長。我們很快檢查了 dmesg 命令,裡面可以查到 HAProxy 程序一些有用的核心資訊。

基本上,HAProxy 程序已經耗盡記憶體。因此,我們決定增加機器記憶體,並將其轉移到 nbproc = 3 的 16 核 64GB 的機器,經過調整,我們終於可以達到 240 萬長連。

後端程式碼

下面是正在使用的後端伺服器程式碼。 我們還在伺服器程式碼中使用 statsd 來獲取客戶端接收的每秒請求的統計資料。

我們還有一個小指令碼執行多個伺服器。 我們有 8 臺機器,每臺機器部署了 10 個後端服務。 我們真的認為有條件的話可以進行無限擴容進行壓測。

客戶端程式碼

對於客戶端,每個 IP 有最大 63k TCP 連線的限制。 如果您不確定這個概念,請參閱本系列之前的文章。

所以為了實現 240 萬個連線(雙向,來自客戶機的是 120 萬),我們需要約 20 臺機器。 我們在所有機器上執行 Vegeta 命令,甚至找到了一種方法來使用像 csshx [3] 這樣的工具,但仍然需要合併所有的 Vegeta 客戶端的結果。

檢視下面的指令碼。

指令碼

Vegeta 提供了名為 pdsh [4] 的工具資訊,可讓您在多臺計算機上同時執行命令。 此外,Vegeta 可以讓我們將多個結果合併成一個,這就是我們想要的。

HAProxy 配置

下面可能是很多讀者最關心的,我們在測試中使用的 HAProxy 配置。 最重要的部分是 nbproc 和 maxconn 引數。 maxconn 設定 HAProxy 允許提供的最大 TCP 連線數(單向)。

對 maxconn 設定的更改導致 HAProxy 程序的 ulimit 增加。 看看下面

HAProxy 配置

最大開啟檔案已增加到 400 萬,因為 HAProxy 的最大連線數設定為 200 萬。

參閱文章 [5] 獲得更多 HAProxy 優化。

HAProxy 優化

相關連結

  1. 系統一 https://medium.com/@sachinmalhotra/load-testing-haproxy-part-1-f7d64500b75d
  2. 系列二 https://medium.com/@sachinmalhotra/load-testing-haproxy-part-2-4c8677780df6
  3. csshx: https://github.com/brockgr/csshx
  4. pdsh: https://github.com/grondo/pdsh
  5. haproxy 配置: https://www.linangran.com/?p=547
  6. Vegeta: https://github.com/tsenart/vegeta
  7. Parallel: http://www.shakthimaan.com/posts/2014/11/27/gnu-parallel/news.html
  8. nbproc setup: http://blog.onefellow.com/post/82478335338/haproxy-mapping-process-to-cpu-core-for-maximum文章來自微信公眾號:高可用架構