您的位置:首頁>正文

Web伺服器如何實現高吞吐低延遲?Dropbox從作業系統到應用層優化指南

這是我在 2017 年 9 月 6 日在 NginxConf 2017 上演講的擴展版。 作為 Dropbox Traffic Team 的 SRE, 我負責邊緣網路優化, 主要包括可靠性, 性能和效率。 Dropbox 邊緣網路[1] 基於 Nginx 代理, 旨在處理延遲敏感的中繼資料事務和高輸送量資料傳輸。 在處理數萬個延遲敏感事務的同時, 處理數以千計的延遲敏感事務, 在整個技術棧中, 從對驅動程式和中斷, 到 TCP/IP 和內核還有庫和應用程式進行效率/性能優化。

在這篇文章中, 我們將討論很多調整 Web 伺服器的方法。 你需要使用科學的方法測量它們的效果, 並確定在你的環境中是否確實起作用。

這不是一篇有關 Linux 性能調優的文章,

即使我將對 bcc 工具, eBPF[2] 和 perf 進行大量的引用, 這絕對不是使用性能分析工具的綜合指南。 如果您想瞭解更多資訊, 您可能需要閱讀 Brendan Gregg 的博客[3]。

這也不是專注於流覽器性能的帖子。 在覆蓋延遲相關的優化時, 我將會觸及用戶端的性能, 但只能簡單地介紹。 如果您想瞭解更多, 您應該閱讀 Ilya Grigorik 的“ 高性能流覽器網路” [4] 。

而且, 這也不是 TLS 最佳實踐彙編。 雖然我會提到一些 TLS 庫及其設置, 您和您的安全小組應評估其中每一個修改和設置的性能以及安全性。 您可以使用 Qualys SSL Test [5], 根據當前的最佳做法來進行驗證, 如果您想瞭解更多關於 TLS 的資訊, 請考慮訂閱Feisty Duck Bulletproof TLS 通訊[6]。

本文結構

我們將討論系統在不同層次上的效率/性能優化。 從硬體和驅動程式開始(這些調優可以幾乎應用于任何高負載伺服器),

到 Linux 內核及其 TCP / IP 協議棧(在任何使用 TCP / IP 協議棧的應用都可以嘗試), 最後我們將討論庫和應用程式級別的調整(這些調整主要適用於一般的 Web 伺服器和 Nginx)。

對於每個潛在的優化領域, 我將嘗試給出延遲/輸送量權衡, 監控指南以及最後針對不同工作負載的調整建議。

硬體

(對硬體調優不感興趣的讀者可以直接跳到作業系統部分)

CPU

如果需要提高非對稱 RSA / EC 的性能, 則需要至少具有 AVX2( avx2 in /proc/cpuinfo )支援的處理器, 最適合的是具有大整數運算 [7] 能力的硬體(支援 bmi 和 adx )的處理器。 對於對稱加密的情況, 需要支援 AES-NI(針對 AES)和 AVX512(針對 ChaCha + Poly)的硬體。 英特爾有一個針對 OpenSSL 1.0.2 在不同硬體產品性能比較 。

延遲敏感的場景, 將受益於更少的 NUMA 節點和禁用 HT。

高輸送量的任務在多核心上運行得更好, 並且將受益於超執行緒(除非它們具有緩存限制), 並且通常不會關心 NUMA。

具體來說, 如果選用英特爾硬體, 你至少要找 Haswell / Broadwell(理想情況需要 Skylake CPU)。 如果您正在使用 AMD, EPYC 的表現非常好。

NIC

在這裡你至少要找 10G, 最好甚至 25G。 如果您想通過單個伺服器 (TLS) 進行推送, 則此處所述的調整將不夠, 您可能需要將 TLS 框架調整進入內核級別(例如 FreeBSD , Linux )。

在軟體方面, 您應該查找具有活動郵寄清單和用戶社區的開源驅動程式。 如果您將調試與驅動程式有關的問題, 這非常重要。

記憶體

這裡的經驗法則是延遲敏感的任務需要更快的記憶體, 而輸送量敏感的任務需要更多的記憶體。

硬碟

這取決於您的緩衝/緩存要求,

但如果要緩存很多檔, 您應該使用快閃記憶體。 有些甚至使用專門的 Flash 友好的檔案系統(通常是日誌結構的), 但是它們未必比普通的 ext4 / xfs 更好。

無論如何, 請不要因為您忘記了啟用 TRIM 或更新固件而引起一些故障。

作業系統:低級別

固件

您應該保持您的固件是最新的, 以避免痛苦和冗長的故障排除過程。 嘗試使用最新的 CPU 微碼, 主機板, 網卡和固態硬碟的固件。 這並不意味著你需要保持最新固件, 這裡的經驗法則是運行次新固件, 除非它在最新版本中修復了嚴重錯誤。

驅動程式

這裡的更新規則與固件幾乎相同。 儘量保持次新的狀態。 這裡的一個注意事項是嘗試將內核升級與驅動程式更新分離。 例如, 您可以使用 DKMS 打包驅動程式,

也可以為您使用的所有內核版本預先編譯驅動程式。 這樣當您更新內核並且某些功能無法正常工作時, 您只需少量處理故障。

CPU

這裡最好的助力是內核倉庫和工具。 在 Ubuntu/Debian 中, 您可以安裝 Linux-tools 套裝軟體, 其中包含幾個 Linux-tools , 但現在我們只使用 cpupower, x86_energy_perf_policy 和 x86_energy_perf_policy 。 為了驗證與 CPU 相關的優化, 您可以使用自己喜歡的負載生成工具(例如, Yandex 使用 Yandex.Tank )對軟體進行壓力測試。 以下是來自開發人員的最新 NginxConf 的演示文稿, 介紹了有關 Nginx loadtesting 最佳做法的內容:“Nginx 性能測試” [7] 。

cpupower

使用這個工具比抓取 /proc/ 更容易。 要查看有關您的處理器及其調速器的資訊:

檢查是否啟用了 Turbo Boost, 對於 Intel CPU, 請確保您正在運行 intel_pstate, 而不是acpi-cpufreq, 甚至是pcc-cpufreq。 如果您仍然使用acpi-cpufreq, 那麼您應該升級內核, 否則可能會導致使用performance 調節器。 當使用intel_pstate 運行時,甚至intel_pstate 應該表現得更好,但是您需要自己驗證。

說到空閒,看看 CPU 發生了什麼改變,你可以使用 turbostat 直接查看處理器的MSR並獲取電源,頻率和空閒狀態資訊:

在這裡,您可以看到實際的 CPU 頻率, 以及核心/包空閒狀態 。

如果即使使用intel_pstate驅動程式,CPU 會花費比您想像的更多的閒置時間,您可以:

將 governor 設為 performance 。

將 x86_energy_perf_policy 設置為性能模式。

或者,只有對於非常延遲的關鍵任務,您可以:

使用 /dev/cpu_dma_latency 介面。

對於 UDP 流量,請使用 busy-polling 。

您可以從 LinuxCon Europe 2015 瞭解更多關於處理器電源管理和P狀態相關資料,特別是英特爾 OpenSource 技術中心演示文稿“ 平衡 Linux 內核中的功能和性能 ”。

CPU 親和性

您可以通過在每個執行緒/進程上應用 CPU 關聯來額外減少延遲,例如,nginx 具有 worker_cpu_affinity 指令,可以將每個 Web 伺服器進程自動綁定到其自身的核心。 這應該可以消除CPU遷移,減少快取記憶體未命中和頁面缺失。 所有這一切都可以通過 perf stat 驗證。

可悲的是,啟用親和力也可能會因為增加進程花費等待可用 CPU 的時間量來對性能產生負面影響。 這可以通過在您的一個 nginx 工作者的 PID 上運行 runqlat 進行監控:

如果您看到多毫秒的尾部延遲,那麼除了 nginx 本身之外,您的伺服器上可能還有太多東西,而CPU親和力會增加延遲,而不是減少它。

記憶體

所有記憶體調優通常是通用的,只有幾件事要推薦:

將 THP 設置為 madvise 並且只有確定它們是有益的時候才能使用它們 ,否則可能會減速一個數量級。

除非您只使用一個 NUMA 節點,否則您應該將 vm.zone_reclaim_mode 設置為 0. ## NUMA

現代 CPU 實際上是通過非常快速的互連連接的多個獨立的 CPU,並且共用各種資源,從 HT 內核上的 L1 緩存開始,到 L3 緩存,到插槽內的記憶體和 PCIe 鏈路。這基本上是 NUMA:具有快速互連的多個執行和存儲單元。

有關 NUMA 及其影響的綜合概述,請參閱 Frank Denneman 的系列文章。

然而長話短說,你可以選擇:

忽略它 ,通過在BIOS中禁用它或在numactl --interleave=all下運行您的軟體,。

拒絕它,通過使用單節點伺服器, 就像Facebook的OCP Yosemite platform 。

擁抱它,通過優化CPU/記憶體放置在使用者和內核空間。

讓我們來談談第三個選擇,因為前兩個沒有太多的優化空間。

要正確使用 NUMA,您需要將每個 numa 節點視為單獨的伺服器,因為您應該首先檢查拓撲,這可以通過 numactl --hardware 來完成:

需要考慮到:

節點數量

每個節點的記憶體大小。

每個節點的CPU數量。

節點之間的距離。

這是一個特別糟糕的例子,因為它有4個節點以及沒有連接記憶體的節點。 在不犧牲系統一半內核的情況下,將每個節點視為單獨的伺服器是不可能的。

我們可以通過使用numastat來驗證:

您還可以要求 numastat 以 /proc/meminfo 格式輸出每節點記憶體使用情況統計資訊:

現在看看一個更簡單拓撲的例子。

由於節點大部分是對稱的,所以我們可以使用 numactl --cpunodebind=X --membind=X 將每個 NUMA 節點的應用程式綁定到另一個埠上,這樣可以利用兩個節點獲得更好的輸送量並通過保留記憶體局部性來優化延遲。

您可以通過延遲記憶體操作來驗證 NUMA 佈局效率,例如通過使用 bcc 的 funclatency 來測量記憶體重操作的延遲,例如 memmove 。

在內核方面,您可以通過使用 perf stat 查看效率,並查找相應的記憶體和調度程式事件:

針對網路繁重工作負載的 NUMA 相關優化的最後一點是,網卡是 PCIe 設備,每個設備都綁定到其自己的 NUMA 節點,因此一些 CPU 在與網路通話時具有較低的延遲。 當我們討論 NIC/CPU 親和性時,我們將討論可以應用於此的優化,但現在可以將交換機切換到 PCI-Express。

PCIe

通常,除非您有某種硬體故障,否則您不需要太深入研究 PCIe 故障排除 。通過為您的 PCIe 設備創建“鏈路寬度”,“鏈路速度”以及可能的 RxErr / BadTLP 警報,可以節省您的故障排除時間。 您可以使用 lspci :

如果您有多個高速設備競爭頻寬(例如,將快速網路與快速存儲組合在一起)時,PCIe可能會成為一個瓶頸,因此您可能需要通過CPU物理分割PCIe設備以獲得最大的輸送量。

另請參閱 Mellanox 網站上的文章“瞭解最佳性能的 PCIe 配置”,這將進一步深入到 PCIe 配置中,如果您在網卡和作業系統之間觀察到資料包丟失,在更高的速度下本文會有所説明。

英特爾指出有時 PCIe 電源管理(ASPM)可能導致更高的延遲,從而導致更高的資料包丟失。 您可以通過將 pcie_aspm=off 添加到內核 cmdline 來禁用它。

NIC

在我們開始之前,值得一提的是, Intel和Mellanox都有自己的性能調優指南,無論您選擇哪種供應商,閱讀它們都是有益的。 驅動程式通常還有自己的README和一組有用的實用程式。

作業系統手冊也需要仔細閱讀,例如“紅帽企業Linux網路性能調優指南”,它解釋了下面提到的大多數優化。

Cloudflare 還有一篇關於調整網路堆疊的一部分的好文章 ,儘管它主要針對低延遲的用例。

當優化 NIC 時,ethtool 非常有幫助。

這裡的一個小筆記:如果您使用的是較新的內核,對於網路操作,您可能需要更新版本的ethtool , iproute2和iptables / nftables套裝軟體。

可以通過 ethtool -S 瞭解網卡上發生的動作:

請諮詢您的NIC製造商瞭解詳細的統計描述,例如Mellanox 為他們提供專門的維琪頁面。

從內核方面看,你會看到 /proc/interrupts, /proc/softirqs 和 /proc/net/softnet_stat。 這裡有兩個有用的 bcc 工具: hardirqs 和 softirqs 。 您優化網路的目標是調整系統,直到您的 CPU 使用率最少,同時沒有丟包。

中斷親和性

這裡的調優通常是從處理器間擴展中斷開始。 你應該如何具體地調整取決於你的目標:

為了最大的輸送量,您可以在系統中的所有NUMA節點上分配中斷。

為了最小化延遲,您可以將中斷限制到單個NUMA節點。 要做到這一點,您可能需要減少佇列數以適應單個節點(這通常意味著用ethtool -L將其數量減少一半)。

供應商通常提供腳本來做到這一點,例如 Intel 有 set_irq_affinity 。

緩衝區大小

網卡需要與內核交換資訊。 這通常通過稱為“環”的資料結構完成,通過ethtool -g查看該環的當前/最大大小:

您可以使用 -G 調整這些預設值。 一般來說,數值越大越好,因為它將為您提供更多的針對突發性保護,從而減少由於沒有緩衝區空間/錯過中斷而導致的丟棄資料包的數量。 但有幾個注意事項:

在舊的內核或沒有 BQL 支援的驅動程式上,高值可能導致 tx 端的更高的緩衝區。

更大的緩衝區也會增加緩存壓力 ,所以如果你遇到了,請嘗試降低它們。

Coalescing 聚合

中斷合併允許您通過在單個中斷中聚合多個事件來延遲內核關於新事件的通知。 當前設置可以通過 ethtool -c 查看:

您可以使用靜態限制,硬限制每個內核每秒中斷的最大數量,或者取決於硬體根據輸送量自動調整中斷速率 。

啟用合併(使用 -C )將增加延遲並可能引入資料包丟失。 另一方面,完全禁用它可能導致中斷限制,從而限制您的性能。

Offloads

現代網卡比較聰明,可以將大量的工作卸載到硬體或驅動程式本身。

所有可能的卸載都可以用 ethtool -k 獲得:

在輸出中,所有不可調的卸載都標有[fixed]尾碼。

這裡有一些經驗法則:

不要啟用 LRO,而是使用 GRO。

對 TSO 要謹慎,因為它高度依賴於您的驅動程式/固件的品質。

不要在舊的內核上啟用 TSO / GSO,因為它可能會導致過多的緩衝區。 所有現代 NIC 都針對多核硬體進行了優化 ,因此內部將資料包分為虛擬佇列,通常為每個 CPU 一個。 當它在硬體中完成時,稱為 RSS,當 OS 負責 CPU 間的資料包負載均衡時,稱為 RPS(其 TX 對應物稱為 XPS)。 當作業系統也試圖智慧化並且路由流程到當前處理該通訊端的 CPU 時,稱為 RFS。 當硬體這樣做時,它被稱為“加速 RFS”或簡稱為 aRFS。

以下是我們生產中的幾種最佳做法:

您正在使用較新的 25G+ 硬體,它可能具有足夠的佇列和巨大的間接表,以便能夠在所有內核上進行 RSS。 一些較舊的網卡具有僅使用前 16 個 CPU 的局限性。

您可以嘗試通過以下方式啟用 RPS:

如果您正在使用較新的 25G+ 硬體,它可能具有足夠的佇列和巨大的間接表,以便能夠在所有內核中使用 RSS 。 一些較舊的網卡具有僅使用前 16 個CPU的局限性。

您可以嘗試啟用 RPS :

您具有比硬體佇列更多的CPU,並且您要犧牲輸送量的延遲。

您正在使用內部隧道(例如 GRE / IPinIP),NIC 不能 RSS;

如果您的CPU相當舊且沒有 x2APIC,請勿啟用 RPS 。

通過 XPS 將每個 CPU 綁定到自己的 TX 佇列通常是一個好主意。

RFS 的有效性高度依賴於您的工作負載,以及是否對其應用 CPU 親和性。

Flow Director 和 ATR

啟用 flow director(或英特爾術語中的 fdir ) 預設情況下在應用程式定向路由模式下運行,該模式通過將資料包和轉向流採樣到實際處理核心的方式實現 aFS 。 它的統計資料也可以通過 ethtool -S 看到。

雖然英特爾聲稱 fdir 在某些情況下會提高性能,但外部研究表明, 它還可以引入高達 1% 的資料包重新排序 ,這對 TCP 性能來說可能是非常有害的。 因此,請測試該特性,並查看 FD 是否對您的工作負載有用,同時注意 TCPOFOQueue 計數器。

作業系統:網路棧

有無數的書籍,視頻和教程來調優 Linux 網路棧。 最近的內核版本不需要像 10 年前那樣運行那麼多的調整,大多數新的 TCP / IP 功能默認啟用和調優,但是人們仍然將其古老的 sysctls.conf 複製粘貼到新內核。

為了驗證網路相關優化的有效性,您應該:

通過 /proc/net/snmp 和 /proc/net/netstat 收集系統級 TCP 指標。

從 ss -n --extended --info getsockopt( TCP_INFO ) 或從您的網路服務器內調用getsockopt( TCP_INFO ) / getsockopt( TCP_CC_INFO ) 獲取的每個連接的總體度量指標。

tcptrace (1)採樣 TCP 流。

從應用/流覽器分析 RUM 指標。

關於網路優化的資訊來源,我通常會喜歡和做 CDN 人一起交談,因為他們一般都知道他們正在做什麼 。 聆聽 Linux 內核開發人員對網路的看法也是非常有啟發性的 。

值得一提的是,由 PackageCloud 強調了對 Linux 網路堆疊的深入瞭解,特別是因為它們將監控重點放在監控上,而不是盲目調整:

監控和調優 Linux 網路堆疊:接收資料

監控和調優 Linux 網路堆疊:發送資料

在我們開始之前,讓我再說一遍: 升級你的內核 ! 因為新版內核有大量新的網路堆疊改進。 我正在談論的新熱點如:TSO 自動化,FQ,TLP 和 RACK 等。 通過升級到新內核,您將獲得一系列可擴展性改進,例如: 刪除路由緩存 , 無鎖監聽通訊端 , SO_REUSEPORT 等等 。

概觀

從最近的 Linux 網路文章中,脫穎而出的是“ 快速製作 Linux TCP ”,它通過將 Linux 發送端的 TCP 協議棧分解成功能塊,將 4 年內的 Linux 內核改進整合在一起:

公平排隊和 Pacing

公平排隊負責提高公平性,減少TCP流之間的線路阻塞,從而對資料包丟失率產生積極的影響。 起搏通過擁塞控制設置的速率調度分組,時間間隔相等,從而進一步減少資料包丟失,從而提高輸送量。

作為附注:公平排隊和 Pacing 可通過 fq qdisc 在 linux 中使用。 有些人可能知道這些是 BBR 的要求,但是它們都可以與 CUBIC 一起使用,從而可以降低 15-20% 的丟包率,從而降低基於丟失的 CC 的輸送量。 只是不要在舊的內核(<3.19)中使用它。

TSO 自動化和 TSQ

這兩者都負責限制TCP堆疊內的緩衝,從而減少延遲,而且不會犧牲輸送量。

擁塞控制

CC演算法本身就是一個很大課題。 這裡是其中一些: tcp_cdg ( CAIA ), tcp_nv (Facebook)和 tcp_bbr (Google)。 我們不會太深入討論他們到底是如何工作的,我們只要說所有這些都更依賴于延遲增加。

BBR 可以說是所有新的擁塞控制中最實用的。 基本思想是基於分組傳輸速率創建網路路徑的模型,然後執行控制環路以最大化頻寬,同時最小化 rtt。 這正是我們尋求已久的。

我們的邊緣 PoP 上 BBR 實驗的初步資料顯示檔下載速度有所增加:

東京6小時TCP BBR實驗PoP:x軸 - 時間,y軸 - 客 戶端下載速度

在這裡我想強調,我們觀察到有明顯的速度增長。 後端更改不是這樣。 這些通常只會惠及p90 +用戶(互聯網連線速度最快的用戶),因為我們認為所有其他用戶已經被限制了頻寬。 諸如改變擁塞控制或啟用FQ /起搏的網路級調優表明,用戶沒有受到頻寬的限制,但如果我能這麼說,它們是“TCP限制的”。

如果您想瞭解更多有關BBR的資訊, APNIC有一個良好的入門級概述 (以及與丟失的擁塞控制的比較)。 有關BBR的更多詳細資訊,您可能需要閱讀bbr-dev郵寄清單存檔。 對於一般對擁塞控制感興趣的人來說,跟蹤擁塞控制研究小組活動可能很有趣。

ACK處理和丟失檢測

足夠的擁塞控制之後,讓我們來談談關於丟失檢測的問題,這裡運行最新的內核將會有所幫助。 新的啟發式演算法如TLP和RACK非常游泳。它們將預設啟用,因此您不需要在升級後調整任何系統設置。

用戶空間優先順序和HOL

用戶空間 socket API 提供隱式緩衝,並且無法在發送之後重新排序塊,因此在多工方案(例如 HTTP/2)中,這可能導致 HOL 阻塞和 h2 優先順序的反轉。TCP_NOTSENT_LOWAT 通訊端選項(和相應的 net.ipv4.tcp_notsent_lowat sysctl) 旨在通過設置閾值來解決這個問題 (即epoll會對您的應用程式造成的影響)。 這可以解決 HTTP/2 優先順序排序的問題,但也可能會對輸送量造成負面影響,因此您可以自己的情況評估。

sysctl

沒有提到需要調整的sysctl,不能簡單地進行網路優化。 但是讓我先從你不想觸摸的東西開始:

net.ipv4.tcp_tw_recycle=1 - 不要使用 。

net.ipv4.tcp_timestamps=0不要禁用它們,除非您知道所有副作用,並且您可以使用它們。

至於您應該使用的sysctls:

net.ipv4.tcp_slow_start_after_idle=0 - 空閒之後的緩慢啟動的主要問題是“空閒”被定義為一個太小的RTO。

net.ipv4.tcp_mtu_probing=1 - 如果您和您的用戶端之間存在ICMP黑洞 (很可能存在),則很有用。

net.ipv4.tcp_rmem , net.ipv4.tcp_wmem應該調整為適合BDP。

echo 2 > /sys/module/tcp_cubic/parameters/hystart_detect - 如果您使用fq + cubic,這可能有助於tcp_cubic過早退出慢啟動 。

還值得注意的是,Daniel Stenberg的RFC草案(儘管有點不活躍),名為TCP Tuning for HTTP ,它試圖將所有可能對HTTP有利的系統調整集中在一個地方。

應用程度:中級工具

就像內核一樣,擁有最新的用戶空間非常重要。 您應該開始升級您的工具,例如您可以打包更新版本的 perf , bcc 等。

一旦你有了新的工具,就可以正確調整和觀察系統的行為了。 通過這一部分的帖子,我們將主要依靠從頭到尾的簡單剖析 , CPU 上的火焰圖以及來自 bcc 的 adhoc 長條圖。

編譯器工具鏈

如果您要編譯硬體優化的程式集,那麼使用現代化的編譯器工具鏈是至關重要的,該程式集存在於Web伺服器通常使用的許多庫中。除了性能之外,較新的編譯器還具有新的安全功能。

系統庫

它也值得升級系統庫,如glibc,因為否則你可能會錯過最近在-lc , -lm , -lrt等的低級函數中進行的優化 。

Zlib

通常Web伺服器將負責壓縮。 根據代理伺服器的資料量,您可能會偶爾在perf top看到zlib,例如:

有一些優化方法可以在最低級別進行優化: 英特爾和Cloudflare以及獨立的zlib-ng專案都有其zlib分支,通過使用新的指令集來提供更好的性能。

malloc

在討論到目前為止的優化之前,我們一直主要面向CPU,但是我們來討論與記憶體相關的優化。 如果您使用大量的Lua與FFI或重的協力廠商模組進行自己的記憶體管理,則可能會因為碎片而觀察到記憶體使用量增加。 您可以嘗試通過切換到jemalloc或tcmalloc來解決該問題。

使用自訂 malloc 也有以下好處:

將 nginx 二進位檔案與環境分開,以便 glibc 版本升級和作業系統遷移會影響更少。

更好的反思 , 分析和統計 。

如果您在 nginx 配置中使用了許多複雜的規則運算式,或者很大程度上依賴於 Lua,則可能會在 perf top 看到 pcre 相關的符號。 您可以通過使用 JIT 編譯 PCRE 來優化,也可以通過pcre_jit on;在nginx中進行pcre_jit on; 。

您可以通過查看火焰圖或使用funclatency來檢查優化結果:

TLS

TLS性能優化可能非常有價值。 我們將著重討論調優伺服器端的效率。

所以現在您需要決定使用哪個TLS庫:Vanilla OpenSSL ,OpenBSD的LibreSSL或Google的BoringSSL 。 選擇TLS庫後,您需要正確構建它:例如,OpenSSL具有一堆內置啟動模式,可根據構建環境進行優化 ; BoringSSL具有確定性的構建,但可悲的是,這種方式更保守,並且預設情況下禁用一些優化 。 無論如何,這裡選擇現代CPU應該終於得到回報:大多數TLS庫可以利用AES-NI和SSE到ADX和AVX512等硬體加速。 您可以使用TLS庫附帶的內置性能測試,例如BoringSSL的bssl speed 。

大多數的性能提升不是來自你的硬體,而是來自您將要使用的密碼套件,因此您必須仔細地優化它們。這裡的變化可以影響您的Web伺服器,最快的密碼套件的安全性並不一定是最好的。如果不確定是什麼加密設置來使用,Mozilla的SSL配置生成器是一個良好的開端。

非對稱加密

如果你的服務上的優勢,那麼你可能會發現相當數量的TLS握手,因此有你的CPU通性能在非對稱加密上會有很大的消耗,優化這裡就成了我們的目的。

為了優化伺服器端的CPU使用可以切換到ECDSA證書,其通常比10倍RSA快。ECDSA比較小,因此可以加速加速握手。但ECDSA也嚴重依賴於系統的亂數發生器的品質,因此,如果您正在使用OpenSSL,確保有足夠的熵(如果使用BoringSSL 你不必擔心)。

作為一個側面說明,但值得一提的是更大的並不總是更好,例如使用RSA 4096個將證降低一個數量級的性能指標:

更糟糕的是,小的不一定是最好的選擇之一:通過使用非公共P-224場為ECDSA你會相比,更常見的P-256得到更糟糕的表現:60%

這裡的經驗法則是,最常用的加密通常是最優化的。

當運行適當優化OpenTLS(基於庫的使用RSA證書),你應該會看到下面的痕跡perf top:AVX2能力,但沒有ADX 的CPU(如Haswell的)應該使用AVX2代碼路徑:

新的硬體應該使用一個通用的蒙哥馬利乘法ADX代碼路徑:

對稱加密

如果您有批量轉讓的大量的如視頻,照片,或更一般的檔,那麼你可以開始觀察分析器的輸出對稱加密符號。在這裡,你只需要確保你的CPU有AES-NI支援,並且您設置了伺服器端的喜好AES-GCM密碼。適當調整硬體應該有以下perf top:

不僅是你的伺服器需要處理的加密/解密,用戶端也能夠減小CPU消耗。如果沒有硬體加速,這可能是非常具有挑戰性的,因此,你可以考慮使用被設計成無需硬體加速快的演算法,如ChaCha20-Poly1305。

ChaCha20-Poly1305在BoringSSL支援開箱即用,使用OpenSSL 1.0.2的話可以考慮使用CloudFlare的補丁。BoringSSL還支援“ 具有相同的優先密碼組 ”,所以您可以使用下面的配置,讓客戶決定根據自己的硬體功能決定:

應用程式級別:高層

要分析該級別的優化效果,你需要收集 RUM 資料。在流覽器中,你可以使用 Navigation Timing APIs 和 Resource Timing APIs。你的主要指標 TTFB 和 TTV / TTI。具有易於可查詢和 graphable 格式將大大簡化重復資料。

壓縮

nginx 的壓縮開始於 mime.types 檔,它定義檔副檔名和 MIME 類型之間默認的對應關係。然後,你需要定義什麼類型使用 gzip_types。如果你想完整的列表,你可以使用 MIME-DB 到自動生成的 mime.types,並與添加這些 .compressible == true 到 gzip_types。

當啟用 gzip,必須注意的是兩個方面:

增加記憶體使用情況。這可以通過限制來解決 gzip_buffers。

增加 TTFB 緩衝。這可以通過 gzip_no_buffer 來解決。

HTTP 壓縮不限於只 gzip 壓縮:nginx 具有協力廠商 ngx_brotli 模組,較 gzip 的壓縮比可以提高 30%。

至於壓縮設置本身,讓我們討論兩個獨立的用例:靜態和動態資料。

對於靜態資料,可以通過預壓縮靜態資產作為構建過程的一部分來歸檔最大壓縮比。

對於動態資料,你需要仔細權衡一個完整的往返:時間壓縮資料+時間以進行傳輸+時間在用戶端上解壓縮。因此,在設置盡可能高的壓縮層級可能是不明智的,不僅是從CPU使用率的角度來看,而且要從TTFB一起看。

代理中的緩衝區可以極大地影響 Web 伺服器的性能,特別是相對於延遲。nginx 的代理模組具有多種緩衝開關。您可以分別控制緩衝 proxy_request_buffering 和 proxy_buffering。如果想啟用緩衝記憶體上消耗上限是設置 client_body_buffer_size 和proxy_buffers 這兩個選項。為了回應這可以通過設置被禁用 proxy_max_temp_file_size 為 0。

緩衝最常用的方法有:

緩衝器請求/回應達到記憶體中的某個閾值之後,溢出到磁片。如果請求啟用了請求緩衝,您只在它完全接收後向後端發送一個請求,並且通過回應緩衝,一旦回應準備就緒,就可以立即釋放後端執行緒。這種方法可以提高輸送量但是會增加回應時間。

無緩衝。緩衝可能不是對延遲敏感路由的一個好選擇,尤其是使用流媒體的情況。對於他們,你可能想禁用它,但現在你的後端需要處理慢客戶機(包括惡意慢/慢讀的攻擊)。

應用通過控制回應緩衝X-Accel-Buffering報頭。

無論您選擇何種方式,不要忘記測試其在TTFB和TTLB效果。此外,如前面提到的,緩衝會影響IO使用情況,甚至後端的利用率,所以要留意這一點。

TLS

現在我們要談論TLS和改善延遲,高層次的方面,可以通過正確配置nginx的完成。大部分優化都在nginx conf 2014的 High Performance Browser Networking’s “Optimizing for TLS” 中談到了,如果不確定,請諮詢Mozilla的伺服器端TLS指南和/或您的安全團隊。

為了驗證你可以使用優化的結果:

WebpageTest 對性能的影響。

SSL 伺服器測試從 Qualys 公司,或 Mozilla TLS 對安全的影響。

會話恢復

作為資料庫管理員愛說“最快的查詢是你什麼也不查”,同樣,TLS 可以緩存的握手結果從而減少一個 RTT。有這樣做的方法有兩種:

你可以要求用戶端存儲所有會話參數(簽名和加密方式),在下一次握手(類似於一個cookie)時將其發送給您。在nginx這邊,通過配置ssl_session_tickets指令。這不不會消耗在伺服器端的任何記憶體,但是有一些缺點的:

你需要的基礎設施來創建,旋轉,並為您的TLS會話分配隨機加密/簽名金鑰。只要記住,你真的不應該1)使用原始程式碼控制存儲票鍵2)生成其它非短暫的物質,如日期或證書這些金鑰。

PFS不會在每個會話的基礎,但每個TLS票鍵的基礎上,因此,如果攻擊者獲取票據金鑰的時候,就可以潛在地解密任何捕獲流量票的時間。

你的密碼將被限制在您的標籤金鑰的大小。如果您正在使用128位的標籤金鑰則沒有多大意義,推薦使用AES256。Nginx的同時支持128位和256位的TLS票鍵。

並非所有的用戶端都支持。

或者你也可以存儲伺服器上的TLS會話參數,只給出一個引用(一個ID)給用戶端。這是通過完成ssl_session_cache指令。它有助於在會話之間保持PFS和大大限制攻擊面。雖然門票鍵有缺點:

在伺服器上,每個會話消耗256位元組的記憶體,這意味著您不能存儲過多的資料。

他們不能在伺服器之間輕鬆共用。因此您可能需要負載等化器,這需要在同一用戶端發送到同一台伺服器保存緩存位置,或者使用ngx_http_lua_module寫一個分散式TLS會話存儲。

作為一個側面說明,如果你用會話票據的辦法,使用 3 個選項:

你將永遠與當前的金鑰加密,但接受與未來都和以前的金鑰加密的會話。

OCSP Stapling

你應該縮短您 OCSP 回應,否則的話:

TLS握手可能需要更長的時間,因為客戶將需要聯繫認證機構來獲取OCSP狀態。

OCSP的故障可能導致可用性降低。

你可能會損害用戶的隱私,因為他們的流覽器將與協力廠商服務聯繫。

OCSP 回應您可以定期從你的憑證授權獲取它,結果分發到您的網路服務器,並與使用它 ssl_stapling_file 的指令:

TLS 記錄大小

TLS資料分解成塊稱為記錄,在您完全接收它之前,無法對其進行驗證和解密。

預設情況下nginx的使用16K塊,甚至不適合IW10擁塞視窗,因此需要額外的往返。nginx提供了一種通過設置記錄大小ssl_buffer_size指令:

為了優化低延遲,你應該把它設置為小塊,比如4K,從CPU使用的角度來看,進非同步減少會更昂貴。

為了優化高通量應設置在16K。

有兩個問題:

您需要手動調整它。

您只能在每個 nginx 配置或每伺服器塊基礎上設置 ssl_buffer_size, 因此, 如果您有一個具有混合延遲/輸送量工作負載的伺服器就搞不定了。

還有另一種方法:動態記錄大小調整。有一個從CloudFlare的Nginx補丁,增加了對動態記錄大小的支援。最初的配置比較痛苦,一旦就緒就一勞永逸。

TLS 1.3

TLS 1.3 功能的確聽起來很不錯,但除非你有資源,否則我建議不啟用它,因為:

它仍然是一個草案。

0-RTT 握手有一定的安全隱患。你的應用程式需要做好準備。

還有中介軟體(防毒軟體,乾粉吸入器等),阻止未知 TLS 版本。

Nginx的是基於事件迴圈的Web伺服器,這意味著它在同一時間內只能做一件事情。儘管似乎它所有的這些事情同時,所有nginx的不只是快速的事件之間切換。它所有的工作,因為處理每個事件只需要幾微秒。但是如果它開已經佔用了太多的時間,延遲可能扶搖直上。

如果你開始注意到你的nginx花費太多的時間內ngx_process_events_and_timers功能,並且分佈是雙峰的,那麼你很可能是由事件迴圈攤位的影響。

AIO 和執行緒池

由於事件迴圈攤位特別是在旋轉磁片的主要來源是 IO,你應該檢查這裡。通過運行 fileslower 可以測量你多少受到它的影響:

為了解決這個問題,Nginx 已經卸載的 IO 執行緒池的支持(也有支持 AIO,但 Unix 本地 AIO 有很多怪癖,所以最好避免它,除非你知道你在做什麼)。基本的設置包括簡單的:

aio threads;
aio_write on;

對於更複雜的情況下,您可以設置自訂thread_pool的,如果一個磁片出故障不會影響請求的其餘部分。執行緒池可以大大減少nginx進程數困在D狀態,改善延遲和輸送量。但這並不能消除eventloop的問題。

日誌寫日誌也可能會導致長時間卡頓,因為這也是在做磁片操作。您可以通過運行ext4slower來檢查,並尋求獲得/錯誤日誌引用:

這是可能通過使用寫他們之前在記憶體中後臺訪問日誌來解決此 buffer 參數的access_log 指令。通過使用gzip 參數,您也可以將它們寫入磁片,寫入磁片前壓縮也可以減少IO壓力。

但要完全消除日誌 IO 影響,寫你應該通過系統日誌,這樣日誌將被完全nginx的事件迴圈集成。

打開檔緩存

由於 open(2) 調用本質上是阻塞的,Web 伺服器通常打開/讀取/關閉檔,因此打開檔的緩存可能是有益的。通過ngx_open_cached_file 可以看到效果:

如果你能看到太多 open 調用或有一些 open 調用花費太多時間,你可以可以考慮打開 open_file_cache:

啟用後 open_file_cache,你可以通過觀察所有的快取記憶體未命中opensnoop,並決定是否需要調整快取記憶體限制:

小結

文中所描述的所有優化都是單個 Web 伺服器框的優化。他們中的一些可以提高可擴展性和性能。如果您希望以最小的延遲服務請求,或者更快地向客戶機傳送的位元組,其他服務也適用。但在我們的經驗中,使用者可見的大量性能來自於一個更高級別的優化,它影響了整個 Dropbox 邊緣網路的行為,如入口/出口流量工程和更聰明的內部負載平衡。這些問題是知識的邊緣,而工業才剛剛開始接近它們。

當使用intel_pstate 運行時,甚至intel_pstate 應該表現得更好,但是您需要自己驗證。

說到空閒,看看 CPU 發生了什麼改變,你可以使用 turbostat 直接查看處理器的MSR並獲取電源,頻率和空閒狀態資訊:

在這裡,您可以看到實際的 CPU 頻率, 以及核心/包空閒狀態 。

如果即使使用intel_pstate驅動程式,CPU 會花費比您想像的更多的閒置時間,您可以:

將 governor 設為 performance 。

將 x86_energy_perf_policy 設置為性能模式。

或者,只有對於非常延遲的關鍵任務,您可以:

使用 /dev/cpu_dma_latency 介面。

對於 UDP 流量,請使用 busy-polling 。

您可以從 LinuxCon Europe 2015 瞭解更多關於處理器電源管理和P狀態相關資料,特別是英特爾 OpenSource 技術中心演示文稿“ 平衡 Linux 內核中的功能和性能 ”。

CPU 親和性

您可以通過在每個執行緒/進程上應用 CPU 關聯來額外減少延遲,例如,nginx 具有 worker_cpu_affinity 指令,可以將每個 Web 伺服器進程自動綁定到其自身的核心。 這應該可以消除CPU遷移,減少快取記憶體未命中和頁面缺失。 所有這一切都可以通過 perf stat 驗證。

可悲的是,啟用親和力也可能會因為增加進程花費等待可用 CPU 的時間量來對性能產生負面影響。 這可以通過在您的一個 nginx 工作者的 PID 上運行 runqlat 進行監控:

如果您看到多毫秒的尾部延遲,那麼除了 nginx 本身之外,您的伺服器上可能還有太多東西,而CPU親和力會增加延遲,而不是減少它。

記憶體

所有記憶體調優通常是通用的,只有幾件事要推薦:

將 THP 設置為 madvise 並且只有確定它們是有益的時候才能使用它們 ,否則可能會減速一個數量級。

除非您只使用一個 NUMA 節點,否則您應該將 vm.zone_reclaim_mode 設置為 0. ## NUMA

現代 CPU 實際上是通過非常快速的互連連接的多個獨立的 CPU,並且共用各種資源,從 HT 內核上的 L1 緩存開始,到 L3 緩存,到插槽內的記憶體和 PCIe 鏈路。這基本上是 NUMA:具有快速互連的多個執行和存儲單元。

有關 NUMA 及其影響的綜合概述,請參閱 Frank Denneman 的系列文章。

然而長話短說,你可以選擇:

忽略它 ,通過在BIOS中禁用它或在numactl --interleave=all下運行您的軟體,。

拒絕它,通過使用單節點伺服器, 就像Facebook的OCP Yosemite platform 。

擁抱它,通過優化CPU/記憶體放置在使用者和內核空間。

讓我們來談談第三個選擇,因為前兩個沒有太多的優化空間。

要正確使用 NUMA,您需要將每個 numa 節點視為單獨的伺服器,因為您應該首先檢查拓撲,這可以通過 numactl --hardware 來完成:

需要考慮到:

節點數量

每個節點的記憶體大小。

每個節點的CPU數量。

節點之間的距離。

這是一個特別糟糕的例子,因為它有4個節點以及沒有連接記憶體的節點。 在不犧牲系統一半內核的情況下,將每個節點視為單獨的伺服器是不可能的。

我們可以通過使用numastat來驗證:

您還可以要求 numastat 以 /proc/meminfo 格式輸出每節點記憶體使用情況統計資訊:

現在看看一個更簡單拓撲的例子。

由於節點大部分是對稱的,所以我們可以使用 numactl --cpunodebind=X --membind=X 將每個 NUMA 節點的應用程式綁定到另一個埠上,這樣可以利用兩個節點獲得更好的輸送量並通過保留記憶體局部性來優化延遲。

您可以通過延遲記憶體操作來驗證 NUMA 佈局效率,例如通過使用 bcc 的 funclatency 來測量記憶體重操作的延遲,例如 memmove 。

在內核方面,您可以通過使用 perf stat 查看效率,並查找相應的記憶體和調度程式事件:

針對網路繁重工作負載的 NUMA 相關優化的最後一點是,網卡是 PCIe 設備,每個設備都綁定到其自己的 NUMA 節點,因此一些 CPU 在與網路通話時具有較低的延遲。 當我們討論 NIC/CPU 親和性時,我們將討論可以應用於此的優化,但現在可以將交換機切換到 PCI-Express。

PCIe

通常,除非您有某種硬體故障,否則您不需要太深入研究 PCIe 故障排除 。通過為您的 PCIe 設備創建“鏈路寬度”,“鏈路速度”以及可能的 RxErr / BadTLP 警報,可以節省您的故障排除時間。 您可以使用 lspci :

如果您有多個高速設備競爭頻寬(例如,將快速網路與快速存儲組合在一起)時,PCIe可能會成為一個瓶頸,因此您可能需要通過CPU物理分割PCIe設備以獲得最大的輸送量。

另請參閱 Mellanox 網站上的文章“瞭解最佳性能的 PCIe 配置”,這將進一步深入到 PCIe 配置中,如果您在網卡和作業系統之間觀察到資料包丟失,在更高的速度下本文會有所説明。

英特爾指出有時 PCIe 電源管理(ASPM)可能導致更高的延遲,從而導致更高的資料包丟失。 您可以通過將 pcie_aspm=off 添加到內核 cmdline 來禁用它。

NIC

在我們開始之前,值得一提的是, Intel和Mellanox都有自己的性能調優指南,無論您選擇哪種供應商,閱讀它們都是有益的。 驅動程式通常還有自己的README和一組有用的實用程式。

作業系統手冊也需要仔細閱讀,例如“紅帽企業Linux網路性能調優指南”,它解釋了下面提到的大多數優化。

Cloudflare 還有一篇關於調整網路堆疊的一部分的好文章 ,儘管它主要針對低延遲的用例。

當優化 NIC 時,ethtool 非常有幫助。

這裡的一個小筆記:如果您使用的是較新的內核,對於網路操作,您可能需要更新版本的ethtool , iproute2和iptables / nftables套裝軟體。

可以通過 ethtool -S 瞭解網卡上發生的動作:

請諮詢您的NIC製造商瞭解詳細的統計描述,例如Mellanox 為他們提供專門的維琪頁面。

從內核方面看,你會看到 /proc/interrupts, /proc/softirqs 和 /proc/net/softnet_stat。 這裡有兩個有用的 bcc 工具: hardirqs 和 softirqs 。 您優化網路的目標是調整系統,直到您的 CPU 使用率最少,同時沒有丟包。

中斷親和性

這裡的調優通常是從處理器間擴展中斷開始。 你應該如何具體地調整取決於你的目標:

為了最大的輸送量,您可以在系統中的所有NUMA節點上分配中斷。

為了最小化延遲,您可以將中斷限制到單個NUMA節點。 要做到這一點,您可能需要減少佇列數以適應單個節點(這通常意味著用ethtool -L將其數量減少一半)。

供應商通常提供腳本來做到這一點,例如 Intel 有 set_irq_affinity 。

緩衝區大小

網卡需要與內核交換資訊。 這通常通過稱為“環”的資料結構完成,通過ethtool -g查看該環的當前/最大大小:

您可以使用 -G 調整這些預設值。 一般來說,數值越大越好,因為它將為您提供更多的針對突發性保護,從而減少由於沒有緩衝區空間/錯過中斷而導致的丟棄資料包的數量。 但有幾個注意事項:

在舊的內核或沒有 BQL 支援的驅動程式上,高值可能導致 tx 端的更高的緩衝區。

更大的緩衝區也會增加緩存壓力 ,所以如果你遇到了,請嘗試降低它們。

Coalescing 聚合

中斷合併允許您通過在單個中斷中聚合多個事件來延遲內核關於新事件的通知。 當前設置可以通過 ethtool -c 查看:

您可以使用靜態限制,硬限制每個內核每秒中斷的最大數量,或者取決於硬體根據輸送量自動調整中斷速率 。

啟用合併(使用 -C )將增加延遲並可能引入資料包丟失。 另一方面,完全禁用它可能導致中斷限制,從而限制您的性能。

Offloads

現代網卡比較聰明,可以將大量的工作卸載到硬體或驅動程式本身。

所有可能的卸載都可以用 ethtool -k 獲得:

在輸出中,所有不可調的卸載都標有[fixed]尾碼。

這裡有一些經驗法則:

不要啟用 LRO,而是使用 GRO。

對 TSO 要謹慎,因為它高度依賴於您的驅動程式/固件的品質。

不要在舊的內核上啟用 TSO / GSO,因為它可能會導致過多的緩衝區。 所有現代 NIC 都針對多核硬體進行了優化 ,因此內部將資料包分為虛擬佇列,通常為每個 CPU 一個。 當它在硬體中完成時,稱為 RSS,當 OS 負責 CPU 間的資料包負載均衡時,稱為 RPS(其 TX 對應物稱為 XPS)。 當作業系統也試圖智慧化並且路由流程到當前處理該通訊端的 CPU 時,稱為 RFS。 當硬體這樣做時,它被稱為“加速 RFS”或簡稱為 aRFS。

以下是我們生產中的幾種最佳做法:

您正在使用較新的 25G+ 硬體,它可能具有足夠的佇列和巨大的間接表,以便能夠在所有內核上進行 RSS。 一些較舊的網卡具有僅使用前 16 個 CPU 的局限性。

您可以嘗試通過以下方式啟用 RPS:

如果您正在使用較新的 25G+ 硬體,它可能具有足夠的佇列和巨大的間接表,以便能夠在所有內核中使用 RSS 。 一些較舊的網卡具有僅使用前 16 個CPU的局限性。

您可以嘗試啟用 RPS :

您具有比硬體佇列更多的CPU,並且您要犧牲輸送量的延遲。

您正在使用內部隧道(例如 GRE / IPinIP),NIC 不能 RSS;

如果您的CPU相當舊且沒有 x2APIC,請勿啟用 RPS 。

通過 XPS 將每個 CPU 綁定到自己的 TX 佇列通常是一個好主意。

RFS 的有效性高度依賴於您的工作負載,以及是否對其應用 CPU 親和性。

Flow Director 和 ATR

啟用 flow director(或英特爾術語中的 fdir ) 預設情況下在應用程式定向路由模式下運行,該模式通過將資料包和轉向流採樣到實際處理核心的方式實現 aFS 。 它的統計資料也可以通過 ethtool -S 看到。

雖然英特爾聲稱 fdir 在某些情況下會提高性能,但外部研究表明, 它還可以引入高達 1% 的資料包重新排序 ,這對 TCP 性能來說可能是非常有害的。 因此,請測試該特性,並查看 FD 是否對您的工作負載有用,同時注意 TCPOFOQueue 計數器。

作業系統:網路棧

有無數的書籍,視頻和教程來調優 Linux 網路棧。 最近的內核版本不需要像 10 年前那樣運行那麼多的調整,大多數新的 TCP / IP 功能默認啟用和調優,但是人們仍然將其古老的 sysctls.conf 複製粘貼到新內核。

為了驗證網路相關優化的有效性,您應該:

通過 /proc/net/snmp 和 /proc/net/netstat 收集系統級 TCP 指標。

從 ss -n --extended --info getsockopt( TCP_INFO ) 或從您的網路服務器內調用getsockopt( TCP_INFO ) / getsockopt( TCP_CC_INFO ) 獲取的每個連接的總體度量指標。

tcptrace (1)採樣 TCP 流。

從應用/流覽器分析 RUM 指標。

關於網路優化的資訊來源,我通常會喜歡和做 CDN 人一起交談,因為他們一般都知道他們正在做什麼 。 聆聽 Linux 內核開發人員對網路的看法也是非常有啟發性的 。

值得一提的是,由 PackageCloud 強調了對 Linux 網路堆疊的深入瞭解,特別是因為它們將監控重點放在監控上,而不是盲目調整:

監控和調優 Linux 網路堆疊:接收資料

監控和調優 Linux 網路堆疊:發送資料

在我們開始之前,讓我再說一遍: 升級你的內核 ! 因為新版內核有大量新的網路堆疊改進。 我正在談論的新熱點如:TSO 自動化,FQ,TLP 和 RACK 等。 通過升級到新內核,您將獲得一系列可擴展性改進,例如: 刪除路由緩存 , 無鎖監聽通訊端 , SO_REUSEPORT 等等 。

概觀

從最近的 Linux 網路文章中,脫穎而出的是“ 快速製作 Linux TCP ”,它通過將 Linux 發送端的 TCP 協議棧分解成功能塊,將 4 年內的 Linux 內核改進整合在一起:

公平排隊和 Pacing

公平排隊負責提高公平性,減少TCP流之間的線路阻塞,從而對資料包丟失率產生積極的影響。 起搏通過擁塞控制設置的速率調度分組,時間間隔相等,從而進一步減少資料包丟失,從而提高輸送量。

作為附注:公平排隊和 Pacing 可通過 fq qdisc 在 linux 中使用。 有些人可能知道這些是 BBR 的要求,但是它們都可以與 CUBIC 一起使用,從而可以降低 15-20% 的丟包率,從而降低基於丟失的 CC 的輸送量。 只是不要在舊的內核(<3.19)中使用它。

TSO 自動化和 TSQ

這兩者都負責限制TCP堆疊內的緩衝,從而減少延遲,而且不會犧牲輸送量。

擁塞控制

CC演算法本身就是一個很大課題。 這裡是其中一些: tcp_cdg ( CAIA ), tcp_nv (Facebook)和 tcp_bbr (Google)。 我們不會太深入討論他們到底是如何工作的,我們只要說所有這些都更依賴于延遲增加。

BBR 可以說是所有新的擁塞控制中最實用的。 基本思想是基於分組傳輸速率創建網路路徑的模型,然後執行控制環路以最大化頻寬,同時最小化 rtt。 這正是我們尋求已久的。

我們的邊緣 PoP 上 BBR 實驗的初步資料顯示檔下載速度有所增加:

東京6小時TCP BBR實驗PoP:x軸 - 時間,y軸 - 客 戶端下載速度

在這裡我想強調,我們觀察到有明顯的速度增長。 後端更改不是這樣。 這些通常只會惠及p90 +用戶(互聯網連線速度最快的用戶),因為我們認為所有其他用戶已經被限制了頻寬。 諸如改變擁塞控制或啟用FQ /起搏的網路級調優表明,用戶沒有受到頻寬的限制,但如果我能這麼說,它們是“TCP限制的”。

如果您想瞭解更多有關BBR的資訊, APNIC有一個良好的入門級概述 (以及與丟失的擁塞控制的比較)。 有關BBR的更多詳細資訊,您可能需要閱讀bbr-dev郵寄清單存檔。 對於一般對擁塞控制感興趣的人來說,跟蹤擁塞控制研究小組活動可能很有趣。

ACK處理和丟失檢測

足夠的擁塞控制之後,讓我們來談談關於丟失檢測的問題,這裡運行最新的內核將會有所幫助。 新的啟發式演算法如TLP和RACK非常游泳。它們將預設啟用,因此您不需要在升級後調整任何系統設置。

用戶空間優先順序和HOL

用戶空間 socket API 提供隱式緩衝,並且無法在發送之後重新排序塊,因此在多工方案(例如 HTTP/2)中,這可能導致 HOL 阻塞和 h2 優先順序的反轉。TCP_NOTSENT_LOWAT 通訊端選項(和相應的 net.ipv4.tcp_notsent_lowat sysctl) 旨在通過設置閾值來解決這個問題 (即epoll會對您的應用程式造成的影響)。 這可以解決 HTTP/2 優先順序排序的問題,但也可能會對輸送量造成負面影響,因此您可以自己的情況評估。

sysctl

沒有提到需要調整的sysctl,不能簡單地進行網路優化。 但是讓我先從你不想觸摸的東西開始:

net.ipv4.tcp_tw_recycle=1 - 不要使用 。

net.ipv4.tcp_timestamps=0不要禁用它們,除非您知道所有副作用,並且您可以使用它們。

至於您應該使用的sysctls:

net.ipv4.tcp_slow_start_after_idle=0 - 空閒之後的緩慢啟動的主要問題是“空閒”被定義為一個太小的RTO。

net.ipv4.tcp_mtu_probing=1 - 如果您和您的用戶端之間存在ICMP黑洞 (很可能存在),則很有用。

net.ipv4.tcp_rmem , net.ipv4.tcp_wmem應該調整為適合BDP。

echo 2 > /sys/module/tcp_cubic/parameters/hystart_detect - 如果您使用fq + cubic,這可能有助於tcp_cubic過早退出慢啟動 。

還值得注意的是,Daniel Stenberg的RFC草案(儘管有點不活躍),名為TCP Tuning for HTTP ,它試圖將所有可能對HTTP有利的系統調整集中在一個地方。

應用程度:中級工具

就像內核一樣,擁有最新的用戶空間非常重要。 您應該開始升級您的工具,例如您可以打包更新版本的 perf , bcc 等。

一旦你有了新的工具,就可以正確調整和觀察系統的行為了。 通過這一部分的帖子,我們將主要依靠從頭到尾的簡單剖析 , CPU 上的火焰圖以及來自 bcc 的 adhoc 長條圖。

編譯器工具鏈

如果您要編譯硬體優化的程式集,那麼使用現代化的編譯器工具鏈是至關重要的,該程式集存在於Web伺服器通常使用的許多庫中。除了性能之外,較新的編譯器還具有新的安全功能。

系統庫

它也值得升級系統庫,如glibc,因為否則你可能會錯過最近在-lc , -lm , -lrt等的低級函數中進行的優化 。

Zlib

通常Web伺服器將負責壓縮。 根據代理伺服器的資料量,您可能會偶爾在perf top看到zlib,例如:

有一些優化方法可以在最低級別進行優化: 英特爾和Cloudflare以及獨立的zlib-ng專案都有其zlib分支,通過使用新的指令集來提供更好的性能。

malloc

在討論到目前為止的優化之前,我們一直主要面向CPU,但是我們來討論與記憶體相關的優化。 如果您使用大量的Lua與FFI或重的協力廠商模組進行自己的記憶體管理,則可能會因為碎片而觀察到記憶體使用量增加。 您可以嘗試通過切換到jemalloc或tcmalloc來解決該問題。

使用自訂 malloc 也有以下好處:

將 nginx 二進位檔案與環境分開,以便 glibc 版本升級和作業系統遷移會影響更少。

更好的反思 , 分析和統計 。

如果您在 nginx 配置中使用了許多複雜的規則運算式,或者很大程度上依賴於 Lua,則可能會在 perf top 看到 pcre 相關的符號。 您可以通過使用 JIT 編譯 PCRE 來優化,也可以通過pcre_jit on;在nginx中進行pcre_jit on; 。

您可以通過查看火焰圖或使用funclatency來檢查優化結果:

TLS

TLS性能優化可能非常有價值。 我們將著重討論調優伺服器端的效率。

所以現在您需要決定使用哪個TLS庫:Vanilla OpenSSL ,OpenBSD的LibreSSL或Google的BoringSSL 。 選擇TLS庫後,您需要正確構建它:例如,OpenSSL具有一堆內置啟動模式,可根據構建環境進行優化 ; BoringSSL具有確定性的構建,但可悲的是,這種方式更保守,並且預設情況下禁用一些優化 。 無論如何,這裡選擇現代CPU應該終於得到回報:大多數TLS庫可以利用AES-NI和SSE到ADX和AVX512等硬體加速。 您可以使用TLS庫附帶的內置性能測試,例如BoringSSL的bssl speed 。

大多數的性能提升不是來自你的硬體,而是來自您將要使用的密碼套件,因此您必須仔細地優化它們。這裡的變化可以影響您的Web伺服器,最快的密碼套件的安全性並不一定是最好的。如果不確定是什麼加密設置來使用,Mozilla的SSL配置生成器是一個良好的開端。

非對稱加密

如果你的服務上的優勢,那麼你可能會發現相當數量的TLS握手,因此有你的CPU通性能在非對稱加密上會有很大的消耗,優化這裡就成了我們的目的。

為了優化伺服器端的CPU使用可以切換到ECDSA證書,其通常比10倍RSA快。ECDSA比較小,因此可以加速加速握手。但ECDSA也嚴重依賴於系統的亂數發生器的品質,因此,如果您正在使用OpenSSL,確保有足夠的熵(如果使用BoringSSL 你不必擔心)。

作為一個側面說明,但值得一提的是更大的並不總是更好,例如使用RSA 4096個將證降低一個數量級的性能指標:

更糟糕的是,小的不一定是最好的選擇之一:通過使用非公共P-224場為ECDSA你會相比,更常見的P-256得到更糟糕的表現:60%

這裡的經驗法則是,最常用的加密通常是最優化的。

當運行適當優化OpenTLS(基於庫的使用RSA證書),你應該會看到下面的痕跡perf top:AVX2能力,但沒有ADX 的CPU(如Haswell的)應該使用AVX2代碼路徑:

新的硬體應該使用一個通用的蒙哥馬利乘法ADX代碼路徑:

對稱加密

如果您有批量轉讓的大量的如視頻,照片,或更一般的檔,那麼你可以開始觀察分析器的輸出對稱加密符號。在這裡,你只需要確保你的CPU有AES-NI支援,並且您設置了伺服器端的喜好AES-GCM密碼。適當調整硬體應該有以下perf top:

不僅是你的伺服器需要處理的加密/解密,用戶端也能夠減小CPU消耗。如果沒有硬體加速,這可能是非常具有挑戰性的,因此,你可以考慮使用被設計成無需硬體加速快的演算法,如ChaCha20-Poly1305。

ChaCha20-Poly1305在BoringSSL支援開箱即用,使用OpenSSL 1.0.2的話可以考慮使用CloudFlare的補丁。BoringSSL還支援“ 具有相同的優先密碼組 ”,所以您可以使用下面的配置,讓客戶決定根據自己的硬體功能決定:

應用程式級別:高層

要分析該級別的優化效果,你需要收集 RUM 資料。在流覽器中,你可以使用 Navigation Timing APIs 和 Resource Timing APIs。你的主要指標 TTFB 和 TTV / TTI。具有易於可查詢和 graphable 格式將大大簡化重復資料。

壓縮

nginx 的壓縮開始於 mime.types 檔,它定義檔副檔名和 MIME 類型之間默認的對應關係。然後,你需要定義什麼類型使用 gzip_types。如果你想完整的列表,你可以使用 MIME-DB 到自動生成的 mime.types,並與添加這些 .compressible == true 到 gzip_types。

當啟用 gzip,必須注意的是兩個方面:

增加記憶體使用情況。這可以通過限制來解決 gzip_buffers。

增加 TTFB 緩衝。這可以通過 gzip_no_buffer 來解決。

HTTP 壓縮不限於只 gzip 壓縮:nginx 具有協力廠商 ngx_brotli 模組,較 gzip 的壓縮比可以提高 30%。

至於壓縮設置本身,讓我們討論兩個獨立的用例:靜態和動態資料。

對於靜態資料,可以通過預壓縮靜態資產作為構建過程的一部分來歸檔最大壓縮比。

對於動態資料,你需要仔細權衡一個完整的往返:時間壓縮資料+時間以進行傳輸+時間在用戶端上解壓縮。因此,在設置盡可能高的壓縮層級可能是不明智的,不僅是從CPU使用率的角度來看,而且要從TTFB一起看。

代理中的緩衝區可以極大地影響 Web 伺服器的性能,特別是相對於延遲。nginx 的代理模組具有多種緩衝開關。您可以分別控制緩衝 proxy_request_buffering 和 proxy_buffering。如果想啟用緩衝記憶體上消耗上限是設置 client_body_buffer_size 和proxy_buffers 這兩個選項。為了回應這可以通過設置被禁用 proxy_max_temp_file_size 為 0。

緩衝最常用的方法有:

緩衝器請求/回應達到記憶體中的某個閾值之後,溢出到磁片。如果請求啟用了請求緩衝,您只在它完全接收後向後端發送一個請求,並且通過回應緩衝,一旦回應準備就緒,就可以立即釋放後端執行緒。這種方法可以提高輸送量但是會增加回應時間。

無緩衝。緩衝可能不是對延遲敏感路由的一個好選擇,尤其是使用流媒體的情況。對於他們,你可能想禁用它,但現在你的後端需要處理慢客戶機(包括惡意慢/慢讀的攻擊)。

應用通過控制回應緩衝X-Accel-Buffering報頭。

無論您選擇何種方式,不要忘記測試其在TTFB和TTLB效果。此外,如前面提到的,緩衝會影響IO使用情況,甚至後端的利用率,所以要留意這一點。

TLS

現在我們要談論TLS和改善延遲,高層次的方面,可以通過正確配置nginx的完成。大部分優化都在nginx conf 2014的 High Performance Browser Networking’s “Optimizing for TLS” 中談到了,如果不確定,請諮詢Mozilla的伺服器端TLS指南和/或您的安全團隊。

為了驗證你可以使用優化的結果:

WebpageTest 對性能的影響。

SSL 伺服器測試從 Qualys 公司,或 Mozilla TLS 對安全的影響。

會話恢復

作為資料庫管理員愛說“最快的查詢是你什麼也不查”,同樣,TLS 可以緩存的握手結果從而減少一個 RTT。有這樣做的方法有兩種:

你可以要求用戶端存儲所有會話參數(簽名和加密方式),在下一次握手(類似於一個cookie)時將其發送給您。在nginx這邊,通過配置ssl_session_tickets指令。這不不會消耗在伺服器端的任何記憶體,但是有一些缺點的:

你需要的基礎設施來創建,旋轉,並為您的TLS會話分配隨機加密/簽名金鑰。只要記住,你真的不應該1)使用原始程式碼控制存儲票鍵2)生成其它非短暫的物質,如日期或證書這些金鑰。

PFS不會在每個會話的基礎,但每個TLS票鍵的基礎上,因此,如果攻擊者獲取票據金鑰的時候,就可以潛在地解密任何捕獲流量票的時間。

你的密碼將被限制在您的標籤金鑰的大小。如果您正在使用128位的標籤金鑰則沒有多大意義,推薦使用AES256。Nginx的同時支持128位和256位的TLS票鍵。

並非所有的用戶端都支持。

或者你也可以存儲伺服器上的TLS會話參數,只給出一個引用(一個ID)給用戶端。這是通過完成ssl_session_cache指令。它有助於在會話之間保持PFS和大大限制攻擊面。雖然門票鍵有缺點:

在伺服器上,每個會話消耗256位元組的記憶體,這意味著您不能存儲過多的資料。

他們不能在伺服器之間輕鬆共用。因此您可能需要負載等化器,這需要在同一用戶端發送到同一台伺服器保存緩存位置,或者使用ngx_http_lua_module寫一個分散式TLS會話存儲。

作為一個側面說明,如果你用會話票據的辦法,使用 3 個選項:

你將永遠與當前的金鑰加密,但接受與未來都和以前的金鑰加密的會話。

OCSP Stapling

你應該縮短您 OCSP 回應,否則的話:

TLS握手可能需要更長的時間,因為客戶將需要聯繫認證機構來獲取OCSP狀態。

OCSP的故障可能導致可用性降低。

你可能會損害用戶的隱私,因為他們的流覽器將與協力廠商服務聯繫。

OCSP 回應您可以定期從你的憑證授權獲取它,結果分發到您的網路服務器,並與使用它 ssl_stapling_file 的指令:

TLS 記錄大小

TLS資料分解成塊稱為記錄,在您完全接收它之前,無法對其進行驗證和解密。

預設情況下nginx的使用16K塊,甚至不適合IW10擁塞視窗,因此需要額外的往返。nginx提供了一種通過設置記錄大小ssl_buffer_size指令:

為了優化低延遲,你應該把它設置為小塊,比如4K,從CPU使用的角度來看,進非同步減少會更昂貴。

為了優化高通量應設置在16K。

有兩個問題:

您需要手動調整它。

您只能在每個 nginx 配置或每伺服器塊基礎上設置 ssl_buffer_size, 因此, 如果您有一個具有混合延遲/輸送量工作負載的伺服器就搞不定了。

還有另一種方法:動態記錄大小調整。有一個從CloudFlare的Nginx補丁,增加了對動態記錄大小的支援。最初的配置比較痛苦,一旦就緒就一勞永逸。

TLS 1.3

TLS 1.3 功能的確聽起來很不錯,但除非你有資源,否則我建議不啟用它,因為:

它仍然是一個草案。

0-RTT 握手有一定的安全隱患。你的應用程式需要做好準備。

還有中介軟體(防毒軟體,乾粉吸入器等),阻止未知 TLS 版本。

Nginx的是基於事件迴圈的Web伺服器,這意味著它在同一時間內只能做一件事情。儘管似乎它所有的這些事情同時,所有nginx的不只是快速的事件之間切換。它所有的工作,因為處理每個事件只需要幾微秒。但是如果它開已經佔用了太多的時間,延遲可能扶搖直上。

如果你開始注意到你的nginx花費太多的時間內ngx_process_events_and_timers功能,並且分佈是雙峰的,那麼你很可能是由事件迴圈攤位的影響。

AIO 和執行緒池

由於事件迴圈攤位特別是在旋轉磁片的主要來源是 IO,你應該檢查這裡。通過運行 fileslower 可以測量你多少受到它的影響:

為了解決這個問題,Nginx 已經卸載的 IO 執行緒池的支持(也有支持 AIO,但 Unix 本地 AIO 有很多怪癖,所以最好避免它,除非你知道你在做什麼)。基本的設置包括簡單的:

aio threads;
aio_write on;

對於更複雜的情況下,您可以設置自訂thread_pool的,如果一個磁片出故障不會影響請求的其餘部分。執行緒池可以大大減少nginx進程數困在D狀態,改善延遲和輸送量。但這並不能消除eventloop的問題。

日誌寫日誌也可能會導致長時間卡頓,因為這也是在做磁片操作。您可以通過運行ext4slower來檢查,並尋求獲得/錯誤日誌引用:

這是可能通過使用寫他們之前在記憶體中後臺訪問日誌來解決此 buffer 參數的access_log 指令。通過使用gzip 參數,您也可以將它們寫入磁片,寫入磁片前壓縮也可以減少IO壓力。

但要完全消除日誌 IO 影響,寫你應該通過系統日誌,這樣日誌將被完全nginx的事件迴圈集成。

打開檔緩存

由於 open(2) 調用本質上是阻塞的,Web 伺服器通常打開/讀取/關閉檔,因此打開檔的緩存可能是有益的。通過ngx_open_cached_file 可以看到效果:

如果你能看到太多 open 調用或有一些 open 調用花費太多時間,你可以可以考慮打開 open_file_cache:

啟用後 open_file_cache,你可以通過觀察所有的快取記憶體未命中opensnoop,並決定是否需要調整快取記憶體限制:

小結

文中所描述的所有優化都是單個 Web 伺服器框的優化。他們中的一些可以提高可擴展性和性能。如果您希望以最小的延遲服務請求,或者更快地向客戶機傳送的位元組,其他服務也適用。但在我們的經驗中,使用者可見的大量性能來自於一個更高級別的優化,它影響了整個 Dropbox 邊緣網路的行為,如入口/出口流量工程和更聰明的內部負載平衡。這些問題是知識的邊緣,而工業才剛剛開始接近它們。

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示