有時多即是少。 當減少負載導致延遲增加時

如在 大多數帖子,一個分散式服務出現了問題,我們稱這個服務為Alvin。 這次不是我自己發現問題,是客戶端的人告訴我的。

有一天,我醒來時收到一封不滿的電子郵件,因為我們計劃在不久的將來推出 Alvin,原因是長時間拖延。 具體來說,客戶的 99% 延遲時間在 50 毫秒左右,遠高於我們的延遲預算。 這是令人驚訝的,因為我對該服務進行了廣泛的測試,特別是在延遲方面,這是一個常見的抱怨。

在將 Alvin 投入測試之前,我以每秒 40k 查詢 (QPS) 的速度進行了大量實驗,所有結果都顯示延遲小於 10 毫秒。 我準備好聲明我不同意他們的結果。 但再看一遍這封信,我發現了一些新的東西:我沒有準確測試他們提到的條件,他們的 QPS 比我低得多。 我測試的 QPS 為 40k,但他們只有 1k。 我又進行了一次實驗,這次 QPS 較低,只是為了安撫他們。

由於我在部落格中討論了這一點,您可能已經發現他們的數字是正確的。 我一遍又一遍地測試我的虛擬客戶端,結果都是一樣:請求數量少不僅會增加延遲,而且會增加延遲超過 10 毫秒的請求數量。 換句話說,如果在 40k QPS 時每秒大約有 50 個請求超過 50 毫秒,那麼在 1k QPS 時每秒有 100 個請求超過 50 毫秒。 悖論!

有時多即是少。 當減少負載導致延遲增加時

縮小搜尋範圍

當在具有許多組件的分散式系統中面臨延遲問題時,第一步是建立一個簡短的嫌疑犯清單。 讓我們更深入地研究一下 Alvin 的架構:

有時多即是少。 當減少負載導致延遲增加時

一個好的起點是已完成的 I/O 轉換清單(網路呼叫/磁碟查找等)。 讓我們試著找出延遲在哪裡。 除了與客戶端進行明顯的 I/O 之外,Alvin 還採取了額外的步驟:存取資料儲存。 然而,該儲存與 Alvin 在同一叢集中運行,因此那裡的延遲應該小於客戶端的延遲。 那麼,嫌疑犯名單如下:

  1. 從客戶端到 Alvin 的網路呼叫。
  2. 從 Alvin 到資料儲存的網路呼叫。
  3. 在資料儲存中的磁碟上搜尋。
  4. 從資料倉儲到 Alvin 的網路呼叫。
  5. Alvin 到客戶端的網路呼叫。

讓我們試著劃掉一些要點。

資料儲存與此無關

我做的第一件事是將 Alvin 轉換為不處理請求的 ping-ping 伺服器。 當它收到請求時,它會傳回一個空響應。 如果延遲減少,那麼 Alvin 或資料倉儲實作中的錯誤並不是聞所未聞的。 在第一個實驗中,我們得到下圖:

有時多即是少。 當減少負載導致延遲增加時

正如您所看到的,使用 ping-ping 伺服器時沒有任何改進。 這意味著資料倉儲不會增加延遲,而嫌疑犯清單減少了一半:

  1. 從客戶端到 Alvin 的網路呼叫。
  2. Alvin 到客戶端的網路呼叫。

偉大的! 該名單正在迅速縮減。 我想我已經差不多明白原因了。

遠程過程調用

現在是時候向您介紹一位新玩家了: 遠程過程調用。 這是 Google 的一個開源程式庫,用於進程內通訊 RPC。 雖然 gRPC 優化良好且廣泛使用,這是我第一次在這種規模的系統上使用它,我預計我的實現至少可以說是次優的。

可用性 gRPC 在堆疊中產生了一個新問題:也許是我的實作或我自己 gRPC 造成延遲問題? 將新嫌疑人加入名單:

  1. 顧客致電圖書館 gRPC
  2. 文庫 gRPC 對客戶端上的庫進行網路調用 gRPC 在服務器上
  3. 文庫 gRPC 聯絡Alvin(乒乓伺服器情況下無操作)

為了讓您了解程式碼的樣子,我的客戶端/Alvin 實作與客戶端-伺服器實作沒有太大區別 非同步範例.

注意:上面的列表有點簡化,因為 gRPC 可以使用您自己的(模板?)線程模型,其中執行堆疊是交織在一起的 gRPC 和用戶實施。 為了簡單起見,我們將堅持使用這個模型。

分析將解決一切問題

劃掉資料儲存後,我以為我快完成了:「現在很簡單! 讓我們應用該配置文件並找出延遲發生的位置。” 我 精密仿形的忠實粉絲,因為 CPU 速度非常快,而且通常不是瓶頸。 大多數延遲發生在處理器必須停止處理以執行其他操作時。 準確的 CPU 分析就是這樣做的:它準確地記錄一切 上下文切換 並明確哪裡發生了延誤。

我在客戶端和伺服器端獲取了四個設定檔:具有高 QPS(低延遲)和具有低 QPS(高延遲)的乒乓伺服器。 為了以防萬一,我還取得了一個範例處理器設定檔。 在比較配置檔案時,我通常會尋找異常的呼叫堆疊。 例如,在高延遲的壞方面,會有更多的上下文切換(10 次或更多)。 但就我而言,上下文切換的次數幾乎相同。 令我恐懼的是,那裡沒有任何重要的東西。

額外的調試

我很絕望。 我不知道我還可以使用哪些其他工具,我的下一個計劃本質上是用不同的變化重複實驗,而不是清楚地診斷問題。

如果什麼

從一開始,我就擔心具體的 50ms 延遲。 這是一個非常重要的時刻。 我決定從程式碼中刪除一些區塊,直到我能夠準確地找出導致此錯誤的部分。 然後進行了一項有效的實驗。

和往常一樣,事後看來,一切都是顯而易見的。 我將客戶端放置在與 Alvin 相同的電腦上 - 並向 localhost。 而延遲的增加消失了!

有時多即是少。 當減少負載導致延遲增加時

網路出現問題。

學習網路工程師技能

我必須承認:我對網路技術的了解很糟糕,尤其是考慮到我每天都與它們一起工作。 但網路是主要嫌疑人,我需要學習如何調試它。

幸運的是,網路喜愛那些想要學習的人。 ping 和tracert 的組合似乎是調試網路傳輸問題的一個足夠好的開始。

首先,我推出了 平視 到 Alvin 的 TCP 連接埠。 我使用預設設定 - 沒什麼特別的。 在 10 多個 ping 中,除了第一個用於預熱的 ping 之外,沒有一個超過 50 毫秒。 這與在第 99 個百分位數觀察到的 100 毫秒延遲增加相反:在那裡,對於每 50 個請求,我們應該看到大約一個請求的延遲為 XNUMX 毫秒。

然後我嘗試了 TRACERT:Alvin 和客戶端之間的路線上的某個節點可能有問題。 但追蹤者也空手而歸。

因此,導致延遲的不是我的程式碼、gRPC 實作或網路。 我開始擔心我永遠無法理解這一點。

現在我們使用什麼作業系統

gRPC 在 Linux 上廣泛使用,但在 Windows 上卻很奇特。 我決定嘗試一個實驗,結果很有效:我創建了一個 Linux 虛擬機,為 Linux 編譯了 Alvin,然後部署了它。

有時多即是少。 當減少負載導致延遲增加時

發生的事情是這樣的:儘管資料來源沒有什麼不同,但 Linux 乒乓球伺服器沒有與類似的 Windows 主機相同的延遲。 事實證明,問題出在 Windows 的 gRPC 實作。

內格爾演算法

一直以來我都以為我丟了一面旗幟 gRPC。 現在我明白它到底是什麼了 gRPC Windows 標誌遺失。 我發現了一個內部 RPC 庫,我相信它可以很好地適用於所有標誌集 Winsock的。 然後我將所有這些標誌添加到 gRPC 中,並在 Windows 上部署 Alvin,在修補的 Windows 乒乓伺服器中!

有時多即是少。 當減少負載導致延遲增加時

幾乎 完成:我開始一次刪除一個新增的標誌,直到回歸返回,以便我可以找出原因。 這是臭名昭著的 TCP_NODELAY,Nagle的演算法切換。

內格爾演算法 嘗試透過延遲訊息傳輸直到資料包大小超過一定位元組數來減少透過網路發送的資料包數量。 雖然這對於普通用戶來說可能很好,但對於即時伺服器來說卻是具有破壞性的,因為作業系統會延遲一些訊息,導致低 QPS 上的延遲。 U gRPC 該標誌是在 TCP 套接字的 Linux 實作中設定的,但在 Windows 中沒有設定。 我是這個 已糾正.

結論

低QPS下的較高延遲是由作業系統優化引起的。 回想起來,分析並沒有偵測到延遲,因為它是在核心模式下完成的,而不是在 使用者模式。 我不知道 Nagle 的演算法是否可以透過 ETW 捕獲來觀察,但這會很有趣。

至於 localhost 實驗,它可能沒有觸及實際的網路程式碼,而且 Nagle 的演算法沒有運行,因此當客戶端透過 localhost 到達 Alvin 時,延遲問題就消失了。

下次當您看到延遲隨著每秒請求數的減少而增加時,Nagle 的演算法應該會出現在您的嫌疑犯名單上!

來源: www.habr.com

添加評論