Web服務器如何實現(xiàn)高吞吐低延遲?Dropbox從操作系統(tǒng)到應用層優(yōu)化指南(高吞吐量實體模塊)
這是我在 2017 年 9 月 6 日在 NginxConf 2017 上演講的擴展版。作為 Dropbox Traffic Team 的 SRE,我負責邊緣網(wǎng)絡優(yōu)化,主要包括可靠性,性能和效率。Dropbox 邊緣網(wǎng)絡[1] 基于 Nginx 代理,旨在處理延遲敏感的元數(shù)據(jù)事務和高吞吐量數(shù)據(jù)傳輸。 在處理數(shù)萬個延遲敏感事務的同時,處理數(shù)以千計的延遲敏感事務,在整個技術棧中,從對驅(qū)動程序和中斷,到 TCP/IP 和內(nèi)核還有庫和應用程序進行效率/性能優(yōu)化。
在這篇文章中,我們將討論很多調(diào)整 Web 服務器的方法。你需要使用科學的方法測量它們的效果,并確定在你的環(huán)境中是否確實起作用。
這不是一篇有關 Linux 性能調(diào)優(yōu)的文章,即使我將對 bcc 工具, eBPF[2] 和 perf 進行大量的引用,這絕對不是使用性能分析工具的綜合指南。 如果您想了解更多信息,您可能需要閱讀 Brendan Gregg 的博客[3]。
這也不是專注于瀏覽器性能的帖子。 在覆蓋延遲相關的優(yōu)化時,我將會觸及客戶端的性能,但只能簡單地介紹。 如果您想了解更多,您應該閱讀 Ilya Grigorik 的“ 高性能瀏覽器網(wǎng)絡” [4] 。
而且,這也不是 TLS 最佳實踐匯編。 雖然我會提到一些 TLS 庫及其設置,您和您的安全小組應評估其中每一個修改和設置的性能以及安全性。 您可以使用 Qualys SSL Test [5],根據(jù)當前的最佳做法來進行驗證,如果您想了解更多關于 TLS 的信息,請考慮訂閱Feisty Duck Bulletproof TLS 通訊[6]。
本文結構
我們將討論系統(tǒng)在不同層次上的效率/性能優(yōu)化。 從硬件和驅(qū)動程序開始(這些調(diào)優(yōu)可以幾乎應用于任何高負載服務器), 到 Linux 內(nèi)核及其 TCP / IP 協(xié)議棧(在任何使用 TCP / IP 協(xié)議棧的應用都可以嘗試),最后我們將討論庫和應用程序級別的調(diào)整(這些調(diào)整主要適用于一般的 Web 服務器和 Nginx)。
對于每個潛在的優(yōu)化領域,我將嘗試給出延遲/吞吐量權衡,監(jiān)控指南以及最后針對不同工作負載的調(diào)整建議。
硬件
(對硬件調(diào)優(yōu)不感興趣的讀者可以直接跳到操作系統(tǒng)部分)
CPU
如果需要提高非對稱 RSA / EC 的性能,則需要至少具有 AVX2( avx2 in /proc/cpuinfo )支持的處理器,最適合的是具有大整數(shù)運算 [7] 能力的硬件(支持 bmi 和 adx )的處理器。 對于對稱加密的情況,需要支持 AES-NI(針對 AES)和 AVX512(針對 ChaCha Poly)的硬件。 英特爾有一個針對 OpenSSL 1.0.2 在不同硬件產(chǎn)品性能比較 。
延遲敏感的場景,將受益于更少的 NUMA 節(jié)點和禁用 HT。 高吞吐量的任務在多核心上運行得更好,并且將受益于超線程(除非它們具有緩存限制),并且通常不會關心 NUMA。
具體來說,如果選用英特爾硬件,你至少要找 Haswell / Broadwell(理想情況需要 Skylake CPU)。 如果您正在使用 AMD, EPYC 的表現(xiàn)非常好。
NIC
在這里你至少要找 10G,最好甚至 25G。 如果您想通過單個服務器 (TLS) 進行推送,則此處所述的調(diào)整將不夠,您可能需要將 TLS 框架調(diào)整進入內(nèi)核級別(例如 FreeBSD , Linux )。
在軟件方面,您應該查找具有活動郵件列表和用戶社區(qū)的開源驅(qū)動程序。 如果您將調(diào)試與驅(qū)動程序有關的問題,這非常重要。
內(nèi)存
這里的經(jīng)驗法則是延遲敏感的任務需要更快的內(nèi)存,而吞吐量敏感的任務需要更多的內(nèi)存。
硬盤
這取決于您的緩沖/緩存要求,但如果要緩存很多文件,您應該使用閃存。 有些甚至使用專門的 Flash 友好的文件系統(tǒng)(通常是日志結構的),但是它們未必比普通的 ext4 / xfs 更好。
無論如何,請不要因為您忘記了啟用 TRIM 或更新固件而引起一些故障。
操作系統(tǒng):低級別
固件
您應該保持您的固件是最新的,以避免痛苦和冗長的故障排除過程。 嘗試使用最新的 CPU 微碼,主板,網(wǎng)卡和固態(tài)硬盤的固件。 這并不意味著你需要保持最新固件,這里的經(jīng)驗法則是運行次新固件,除非它在最新版本中修復了嚴重錯誤。
驅(qū)動程序
這里的更新規(guī)則與固件幾乎相同。 盡量保持次新的狀態(tài)。 這里的一個注意事項是嘗試將內(nèi)核升級與驅(qū)動程序更新分離。 例如,您可以使用 DKMS 打包驅(qū)動程序,也可以為您使用的所有內(nèi)核版本預先編譯驅(qū)動程序。 這樣當您更新內(nèi)核并且某些功能無法正常工作時,您只需少量處理故障。
CPU
這里最好的助力是內(nèi)核倉庫和工具。 在 Ubuntu/Debian 中,您可以安裝 Linux-tools 軟件包,其中包含幾個 Linux-tools ,但現(xiàn)在我們只使用 cpupower, x86_energy_perf_policy 和 x86_energy_perf_policy 。 為了驗證與 CPU 相關的優(yōu)化,您可以使用自己喜歡的負載生成工具(例如, Yandex 使用 Yandex.Tank )對軟件進行壓力測試。以下是來自開發(fā)人員的最新 NginxConf 的演示文稿,介紹了有關 Nginx loadtesting 最佳做法的內(nèi)容:“Nginx 性能測試” [7] 。
cpupower
使用這個工具比抓取 /proc/
更容易。 要查看有關您的處理器及其調(diào)速器的信息:
檢查是否啟用了 Turbo Boost,對于 Intel CPU,請確保您正在運行 intel_pstate
,而不是acpi-cpufreq
,甚至是pcc-cpufreq
。 如果您仍然使用acpi-cpufreq
,那么您應該升級內(nèi)核,否則可能會導致使用performance
調(diào)節(jié)器。 當使用intel_pstate
運行時,甚至intel_pstate
應該表現(xiàn)得更好,但是您需要自己驗證。
說到空閑,看看 CPU 發(fā)生了什么改變,你可以使用 turbostat
直接查看處理器的MSR并獲取電源,頻率和空閑狀態(tài)信息:
在這里,您可以看到實際的 CPU 頻率, 以及核心/包空閑狀態(tài) 。
如果即使使用intel_pstate驅(qū)動程序,CPU 會花費比您想象的更多的空閑時間,您可以:
-
將 governor 設為 performance 。
將 x86_energy_perf_policy 設置為性能模式。
或者,只有對于非常延遲的關鍵任務,您可以:
-
使用 /dev/cpu_dma_latency 接口。
對于 UDP 流量,請使用 busy-polling 。
您可以從 LinuxCon Europe 2015 了解更多關于處理器電源管理和P狀態(tài)相關資料,特別是英特爾 OpenSource 技術中心演示文稿“ 平衡 Linux 內(nèi)核中的功能和性能 ”。
CPU 親和性
您可以通過在每個線程/進程上應用 CPU 關聯(lián)來額外減少延遲,例如,nginx 具有 worker_cpu_affinity 指令,可以將每個 Web 服務器進程自動綁定到其自身的核心。 這應該可以消除CPU遷移,減少高速緩存未命中和頁面缺失。 所有這一切都可以通過 perf stat 驗證。
可悲的是,啟用親和力也可能會因為增加進程花費等待可用 CPU 的時間量來對性能產(chǎn)生負面影響。 這可以通過在您的一個 nginx 工作者的 PID 上運行 runqlat 進行監(jiān)控:
如果您看到多毫秒的尾部延遲,那么除了 nginx 本身之外,您的服務器上可能還有太多東西,而CPU親和力會增加延遲,而不是減少它。
內(nèi)存
所有內(nèi)存調(diào)優(yōu)通常是通用的,只有幾件事要推薦:
-
將 THP 設置為 madvise 并且只有確定它們是有益的時候才能使用它們 ,否則可能會減速一個數(shù)量級。
除非您只使用一個 NUMA 節(jié)點,否則您應該將 vm.zone_reclaim_mode 設置為 0. ## NUMA
現(xiàn)代 CPU 實際上是通過非常快速的互連連接的多個獨立的 CPU,并且共享各種資源,從 HT 內(nèi)核上的 L1 緩存開始,到 L3 緩存,到插槽內(nèi)的內(nèi)存和 PCIe 鏈路。這基本上是 NUMA:具有快速互連的多個執(zhí)行和存儲單元。
有關 NUMA 及其影響的綜合概述,請參閱 Frank Denneman 的系列文章。
然而長話短說,你可以選擇:
-
忽略它 ,通過在BIOS中禁用它或在numactl –interleave=all下運行您的軟件,。
拒絕它,通過使用單節(jié)點服務器, 就像Facebook的OCP Yosemite platform 。
擁抱它,通過優(yōu)化CPU/內(nèi)存放置在用戶和內(nèi)核空間。
讓我們來談談第三個選擇,因為前兩個沒有太多的優(yōu)化空間。
要正確使用 NUMA,您需要將每個 numa 節(jié)點視為單獨的服務器,因為您應該首先檢查拓撲,這可以通過 numactl –hardware 來完成:
需要考慮到:
-
節(jié)點數(shù)量
每個節(jié)點的內(nèi)存大小。
每個節(jié)點的CPU數(shù)量。
節(jié)點之間的距離。
這是一個特別糟糕的例子,因為它有4個節(jié)點以及沒有連接內(nèi)存的節(jié)點。 在不犧牲系統(tǒng)一半內(nèi)核的情況下,將每個節(jié)點視為單獨的服務器是不可能的。
我們可以通過使用numastat來驗證:
您還可以要求 numastat 以 /proc/meminfo 格式輸出每節(jié)點內(nèi)存使用情況統(tǒng)計信息:
現(xiàn)在看看一個更簡單拓撲的例子。
由于節(jié)點大部分是對稱的,所以我們可以使用 numactl –cpunodebind=X –membind=X 將每個 NUMA 節(jié)點的應用程序綁定到另一個端口上,這樣可以利用兩個節(jié)點獲得更好的吞吐量并通過保留內(nèi)存局部性來優(yōu)化延遲。
您可以通過延遲內(nèi)存操作來驗證 NUMA 布局效率,例如通過使用 bcc 的 funclatency 來測量內(nèi)存重操作的延遲,例如 memmove 。
在內(nèi)核方面,您可以通過使用 perf stat 查看效率,并查找相應的內(nèi)存和調(diào)度程序事件:
針對網(wǎng)絡繁重工作負載的 NUMA 相關優(yōu)化的最后一點是,網(wǎng)卡是 PCIe 設備,每個設備都綁定到其自己的 NUMA 節(jié)點,因此一些 CPU 在與網(wǎng)絡通話時具有較低的延遲。 當我們討論 NIC/CPU 親和性時,我們將討論可以應用于此的優(yōu)化,但現(xiàn)在可以將交換機切換到 PCI-Express。
PCIe
通常,除非您有某種硬件故障,否則您不需要太深入研究 PCIe 故障排除 。通過為您的 PCIe 設備創(chuàng)建“鏈路寬度”,“鏈路速度”以及可能的 RxErr / BadTLP 警報,可以節(jié)省您的故障排除時間。 您可以使用 lspci :
如果您有多個高速設備競爭帶寬(例如,將快速網(wǎng)絡與快速存儲組合在一起)時,PCIe可能會成為一個瓶頸,因此您可能需要通過CPU物理分割PCIe設備以獲得最大的吞吐量。
另請參閱 Mellanox 網(wǎng)站上的文章“了解最佳性能的 PCIe 配置”,這將進一步深入到 PCIe 配置中,如果您在網(wǎng)卡和操作系統(tǒng)之間觀察到數(shù)據(jù)包丟失,在更高的速度下本文會有所幫助。
英特爾指出有時 PCIe 電源管理(ASPM)可能導致更高的延遲,從而導致更高的數(shù)據(jù)包丟失。 您可以通過將 pcie_aspm=off 添加到內(nèi)核 cmdline 來禁用它。
NIC
在我們開始之前,值得一提的是, Intel和Mellanox都有自己的性能調(diào)優(yōu)指南,無論您選擇哪種供應商,閱讀它們都是有益的。 驅(qū)動程序通常還有自己的README和一組有用的實用程序。
操作系統(tǒng)手冊也需要仔細閱讀,例如“紅帽企業(yè)Linux網(wǎng)絡性能調(diào)優(yōu)指南”,它解釋了下面提到的大多數(shù)優(yōu)化。
Cloudflare 還有一篇關于調(diào)整網(wǎng)絡堆棧的一部分的好文章 ,盡管它主要針對低延遲的用例。
當優(yōu)化 NIC 時,ethtool 非常有幫助。
這里的一個小筆記:如果您使用的是較新的內(nèi)核,對于網(wǎng)絡操作,您可能需要更新版本的ethtool , iproute2和iptables / nftables軟件包。
可以通過 ethtool -S 了解網(wǎng)卡上發(fā)生的動作:
請咨詢您的NIC制造商了解詳細的統(tǒng)計描述,例如Mellanox 為他們提供專門的維基頁面。
從內(nèi)核方面看,你會看到 /proc/interrupts, /proc/softirqs 和 /proc/net/softnet_stat。 這里有兩個有用的 bcc 工具: hardirqs 和 softirqs 。 您優(yōu)化網(wǎng)絡的目標是調(diào)整系統(tǒng),直到您的 CPU 使用率最少,同時沒有丟包。
中斷親和性
這里的調(diào)優(yōu)通常是從處理器間擴展中斷開始。 你應該如何具體地調(diào)整取決于你的目標:
-
為了最大的吞吐量,您可以在系統(tǒng)中的所有NUMA節(jié)點上分配中斷。
為了最小化延遲,您可以將中斷限制到單個NUMA節(jié)點。 要做到這一點,您可能需要減少隊列數(shù)以適應單個節(jié)點(這通常意味著用ethtool -L將其數(shù)量減少一半)。
供應商通常提供腳本來做到這一點,例如 Intel 有 set_irq_affinity 。
緩沖區(qū)大小
網(wǎng)卡需要與內(nèi)核交換信息。 這通常通過稱為“環(huán)”的數(shù)據(jù)結構完成,通過ethtool -g查看該環(huán)的當前/最大大?。?/p>
您可以使用 -G 調(diào)整這些預設值。 一般來說,數(shù)值越大越好,因為它將為您提供更多的針對突發(fā)性保護,從而減少由于沒有緩沖區(qū)空間/錯過中斷而導致的丟棄數(shù)據(jù)包的數(shù)量。 但有幾個注意事項:
-
在舊的內(nèi)核或沒有 BQL 支持的驅(qū)動程序上,高值可能導致 tx 端的更高的緩沖區(qū)。
更大的緩沖區(qū)也會增加緩存壓力 ,所以如果你遇到了,請嘗試降低它們。
Coalescing 聚合
中斷合并允許您通過在單個中斷中聚合多個事件來延遲內(nèi)核關于新事件的通知。 當前設置可以通過 ethtool -c 查看:
您可以使用靜態(tài)限制,硬限制每個內(nèi)核每秒中斷的最大數(shù)量,或者取決于硬件根據(jù)吞吐量自動調(diào)整中斷速率 。
啟用合并(使用 -C )將增加延遲并可能引入數(shù)據(jù)包丟失。 另一方面,完全禁用它可能導致中斷限制,從而限制您的性能。
Offloads
現(xiàn)代網(wǎng)卡比較聰明,可以將大量的工作卸載到硬件或驅(qū)動程序本身。
所有可能的卸載都可以用 ethtool -k 獲得:
在輸出中,所有不可調(diào)的卸載都標有[fixed]后綴。
這里有一些經(jīng)驗法則:
-
不要啟用 LRO,而是使用 GRO。
對 TSO 要謹慎,因為它高度依賴于您的驅(qū)動程序/固件的質(zhì)量。
不要在舊的內(nèi)核上啟用 TSO / GSO,因為它可能會導致過多的緩沖區(qū)。 所有現(xiàn)代 NIC 都針對多核硬件進行了優(yōu)化 ,因此內(nèi)部將數(shù)據(jù)包分為虛擬隊列,通常為每個 CPU 一個。 當它在硬件中完成時,稱為 RSS,當 OS 負責 CPU 間的數(shù)據(jù)包負載均衡時,稱為 RPS(其 TX 對應物稱為 XPS)。 當操作系統(tǒng)也試圖智能化并且路由流程到當前處理該套接字的 CPU 時,稱為 RFS。 當硬件這樣做時,它被稱為“加速 RFS”或簡稱為 aRFS。
以下是我們生產(chǎn)中的幾種最佳做法:
-
您正在使用較新的 25G 硬件,它可能具有足夠的隊列和巨大的間接表,以便能夠在所有內(nèi)核上進行 RSS。 一些較舊的網(wǎng)卡具有僅使用前 16 個 CPU 的局限性。
您可以嘗試通過以下方式啟用 RPS:
如果您正在使用較新的 25G 硬件,它可能具有足夠的隊列和巨大的間接表,以便能夠在所有內(nèi)核中使用 RSS 。 一些較舊的網(wǎng)卡具有僅使用前 16 個CPU的局限性。
您可以嘗試啟用 RPS :
您具有比硬件隊列更多的CPU,并且您要犧牲吞吐量的延遲。
您正在使用內(nèi)部隧道(例如 GRE / IPinIP),NIC 不能 RSS;
如果您的CPU相當舊且沒有 x2APIC,請勿啟用 RPS 。
通過 XPS 將每個 CPU 綁定到自己的 TX 隊列通常是一個好主意。
RFS 的有效性高度依賴于您的工作負載,以及是否對其應用 CPU 親和性。
Flow Director 和 ATR
啟用 flow director(或英特爾術語中的 fdir ) 默認情況下在應用程序定向路由模式下運行,該模式通過將數(shù)據(jù)包和轉(zhuǎn)向流采樣到實際處理核心的方式實現(xiàn) aFS 。 它的統(tǒng)計數(shù)據(jù)也可以通過 ethtool -S 看到。
雖然英特爾聲稱 fdir 在某些情況下會提高性能,但外部研究表明, 它還可以引入高達 1% 的數(shù)據(jù)包重新排序 ,這對 TCP 性能來說可能是非常有害的。 因此,請測試該特性,并查看 FD 是否對您的工作負載有用,同時注意 TCPOFOQueue 計數(shù)器。
操作系統(tǒng):網(wǎng)絡棧
有無數(shù)的書籍,視頻和教程來調(diào)優(yōu) Linux 網(wǎng)絡棧。 最近的內(nèi)核版本不需要像 10 年前那樣運行那么多的調(diào)整,大多數(shù)新的 TCP / IP 功能默認啟用和調(diào)優(yōu),但是人們?nèi)匀粚⑵涔爬系?sysctls.conf 復制粘貼到新內(nèi)核。
為了驗證網(wǎng)絡相關優(yōu)化的有效性,您應該:
-
通過 /proc/net/snmp 和 /proc/net/netstat 收集系統(tǒng)級 TCP 指標。
從 ss -n –extended –info getsockopt( TCP_INFO ) 或從您的網(wǎng)絡服務器內(nèi)調(diào)用getsockopt( TCP_INFO ) / getsockopt( TCP_CC_INFO ) 獲取的每個連接的總體度量指標。
tcptrace (1)采樣 TCP 流。
從應用/瀏覽器分析 RUM 指標。
關于網(wǎng)絡優(yōu)化的信息來源,我通常會喜歡和做 CDN 人一起交談,因為他們一般都知道他們正在做什么 。 聆聽 Linux 內(nèi)核開發(fā)人員對網(wǎng)絡的看法也是非常有啟發(fā)性的 。
值得一提的是,由 PackageCloud 強調(diào)了對 Linux 網(wǎng)絡堆棧的深入了解,特別是因為它們將監(jiān)控重點放在監(jiān)控上,而不是盲目調(diào)整:
-
監(jiān)控和調(diào)優(yōu) Linux 網(wǎng)絡堆棧:接收數(shù)據(jù)
監(jiān)控和調(diào)優(yōu) Linux 網(wǎng)絡堆棧:發(fā)送數(shù)據(jù)
在我們開始之前,讓我再說一遍: 升級你的內(nèi)核 ! 因為新版內(nèi)核有大量新的網(wǎng)絡堆棧改進。 我正在談論的新熱點如:TSO 自動化,F(xiàn)Q,TLP 和 RACK 等。 通過升級到新內(nèi)核,您將獲得一系列可擴展性改進,例如: 刪除路由緩存 , 無鎖監(jiān)聽套接字 , SO_REUSEPORT 等等 。
概觀
從最近的 Linux 網(wǎng)絡文章中,脫穎而出的是“ 快速制作 Linux TCP ”,它通過將 Linux 發(fā)送端的 TCP 協(xié)議棧分解成功能塊,將 4 年內(nèi)的 Linux 內(nèi)核改進整合在一起:
公平排隊和 Pacing
公平排隊負責提高公平性,減少TCP流之間的線路阻塞,從而對數(shù)據(jù)包丟失率產(chǎn)生積極的影響。 起搏通過擁塞控制設置的速率調(diào)度分組,時間間隔相等,從而進一步減少數(shù)據(jù)包丟失,從而提高吞吐量。
作為附注:公平排隊和 Pacing 可通過 fq qdisc 在 linux 中使用。 有些人可能知道這些是 BBR 的要求,但是它們都可以與 CUBIC 一起使用,從而可以降低 15-20% 的丟包率,從而降低基于丟失的 CC 的吞吐量。 只是不要在舊的內(nèi)核(<3.19)中使用它。
TSO 自動化和 TSQ
這兩者都負責限制TCP堆棧內(nèi)的緩沖,從而減少延遲,而且不會犧牲吞吐量。
擁塞控制
CC算法本身就是一個很大課題。 這里是其中一些: tcp_cdg ( CAIA ), tcp_nv (Facebook)和 tcp_bbr (Google)。 我們不會太深入討論他們到底是如何工作的,我們只要說所有這些都更依賴于延遲增加。
BBR 可以說是所有新的擁塞控制中最實用的。 基本思想是基于分組傳輸速率創(chuàng)建網(wǎng)絡路徑的模型,然后執(zhí)行控制環(huán)路以最大化帶寬,同時最小化 rtt。 這正是我們尋求已久的。
我們的邊緣 PoP 上 BBR 實驗的初步數(shù)據(jù)顯示文件下載速度有所增加:
東京6小時TCP BBR實驗PoP:x軸 – 時間,y軸 – 客戶端下載速度
在這里我想強調(diào),我們觀察到有明顯的速度增長。 后端更改不是這樣。 這些通常只會惠及p90 用戶(互聯(lián)網(wǎng)連接速度最快的用戶),因為我們認為所有其他用戶已經(jīng)被限制了帶寬。 諸如改變擁塞控制或啟用FQ /起搏的網(wǎng)絡級調(diào)優(yōu)表明,用戶沒有受到帶寬的限制,但如果我能這么說,它們是“TCP限制的”。
如果您想了解更多有關BBR的信息, APNIC有一個良好的入門級概述 (以及與丟失的擁塞控制的比較)。 有關BBR的更多詳細信息,您可能需要閱讀bbr-dev郵件列表存檔。 對于一般對擁塞控制感興趣的人來說,跟蹤擁塞控制研究小組活動可能很有趣。
ACK處理和丟失檢測
足夠的擁塞控制之后,讓我們來談談關于丟失檢測的問題,這里運行最新的內(nèi)核將會有所幫助。 新的啟發(fā)式算法如TLP和RACK非常游泳。它們將默認啟用,因此您不需要在升級后調(diào)整任何系統(tǒng)設置。
用戶空間優(yōu)先級和HOL
用戶空間 socket API 提供隱式緩沖,并且無法在發(fā)送之后重新排序塊,因此在多路復用方案(例如 HTTP/2)中,這可能導致 HOL 阻塞和 h2 優(yōu)先級的反轉(zhuǎn)。TCP_NOTSENT_LOWAT 套接字選項(和相應的 net.ipv4.tcp_notsent_lowat sysctl) 旨在通過設置閾值來解決這個問題 (即epoll會對您的應用程序造成的影響)。 這可以解決 HTTP/2 優(yōu)先級排序的問題,但也可能會對吞吐量造成負面影響,因此您可以自己的情況評估。
sysctl
沒有提到需要調(diào)整的sysctl,不能簡單地進行網(wǎng)絡優(yōu)化。 但是讓我先從你不想觸摸的東西開始:
-
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應該調(diào)整為適合BDP。
echo 2 > /sys/module/tcp_cubic/parameters/hystart_detect – 如果您使用fq cubic,這可能有助于tcp_cubic過早退出慢啟動 。
還值得注意的是,Daniel Stenberg的RFC草案(盡管有點不活躍),名為TCP Tuning for HTTP ,它試圖將所有可能對HTTP有利的系統(tǒng)調(diào)整集中在一個地方。
應用程度:中級
工具
就像內(nèi)核一樣,擁有最新的用戶空間非常重要。 您應該開始升級您的工具,例如您可以打包更新版本的 perf , bcc 等。
一旦你有了新的工具,就可以正確調(diào)整和觀察系統(tǒng)的行為了。 通過這一部分的帖子,我們將主要依靠從頭到尾的簡單剖析 , CPU 上的火焰圖以及來自 bcc 的 adhoc 直方圖。
編譯器工具鏈
如果您要編譯硬件優(yōu)化的程序集,那么使用現(xiàn)代化的編譯器工具鏈是至關重要的,該程序集存在于Web服務器通常使用的許多庫中。除了性能之外,較新的編譯器還具有新的安全功能。
系統(tǒng)庫
它也值得升級系統(tǒng)庫,如glibc,因為否則你可能會錯過最近在-lc , -lm , -lrt等的低級函數(shù)中進行的優(yōu)化 。
Zlib
通常Web服務器將負責壓縮。 根據(jù)代理服務器的數(shù)據(jù)量,您可能會偶爾在perf top看到zlib,例如:
有一些優(yōu)化方法可以在最低級別進行優(yōu)化: 英特爾和Cloudflare以及獨立的zlib-ng項目都有其zlib分支,通過使用新的指令集來提供更好的性能。
malloc
在討論到目前為止的優(yōu)化之前,我們一直主要面向CPU,但是我們來討論與內(nèi)存相關的優(yōu)化。 如果您使用大量的Lua與FFI或重的第三方模塊進行自己的內(nèi)存管理,則可能會因為碎片而觀察到內(nèi)存使用量增加。 您可以嘗試通過切換到jemalloc或tcmalloc來解決該問題。
使用自定義 malloc 也有以下好處:
-
將 nginx 二進制文件與環(huán)境分開,以便 glibc 版本升級和操作系統(tǒng)遷移會影響更少。
更好的反思 , 分析和統(tǒng)計 。
如果您在 nginx 配置中使用了許多復雜的正則表達式,或者很大程度上依賴于 Lua,則可能會在 perf top 看到 pcre 相關的符號。 您可以通過使用 JIT 編譯 PCRE 來優(yōu)化,也可以通過pcre_jit on;在nginx中進行pcre_jit on; 。
您可以通過查看火焰圖或使用funclatency來檢查優(yōu)化結果:
TLS
TLS性能優(yōu)化可能非常有價值。 我們將著重討論調(diào)優(yōu)服務器端的效率。
所以現(xiàn)在您需要決定使用哪個TLS庫:Vanilla OpenSSL ,OpenBSD的LibreSSL或Google的BoringSSL 。 選擇TLS庫后,您需要正確構建它:例如,OpenSSL具有一堆內(nèi)置啟動模式,可根據(jù)構建環(huán)境進行優(yōu)化 ; BoringSSL具有確定性的構建,但可悲的是,這種方式更保守,并且默認情況下禁用一些優(yōu)化 。 無論如何,這里選擇現(xiàn)代CPU應該終于得到回報:大多數(shù)TLS庫可以利用AES-NI和SSE到ADX和AVX512等硬件加速。 您可以使用TLS庫附帶的內(nèi)置性能測試,例如BoringSSL的bssl speed 。
大多數(shù)的性能提升不是來自你的硬件,而是來自您將要使用的密碼套件,因此您必須仔細地優(yōu)化它們。這里的變化可以影響您的Web服務器,最快的密碼套件的安全性并不一定是最好的。如果不確定是什么加密設置來使用,Mozilla的SSL配置生成器是一個良好的開端。
非對稱加密
如果你的服務上的優(yōu)勢,那么你可能會發(fā)現(xiàn)相當數(shù)量的TLS握手,因此有你的CPU通性能在非對稱加密上會有很大的消耗,優(yōu)化這里就成了我們的目的。
為了優(yōu)化服務器端的CPU使用可以切換到ECDSA證書,其通常比10倍RSA快。ECDSA比較小,因此可以加速加速握手。但ECDSA也嚴重依賴于系統(tǒng)的隨機數(shù)發(fā)生器的質(zhì)量,因此,如果您正在使用OpenSSL,確保有足夠的熵(如果使用BoringSSL 你不必擔心)。
作為一個側面說明,但值得一提的是更大的并不總是更好,例如使用RSA 4096個將證降低一個數(shù)量級的性能指標:
更糟糕的是,小的不一定是最好的選擇之一:通過使用非公共P-224場為ECDSA你會相比,更常見的P-256得到更糟糕的表現(xiàn):60%
這里的經(jīng)驗法則是,最常用的加密通常是最優(yōu)化的。
當運行適當優(yōu)化OpenTLS(基于庫的使用RSA證書),你應該會看到下面的痕跡perf top:AVX2能力,但沒有ADX 的CPU(如Haswell的)應該使用AVX2代碼路徑:
新的硬件應該使用一個通用的蒙哥馬利乘法ADX代碼路徑:
對稱加密
如果您有批量轉(zhuǎn)讓的大量的如視頻,照片,或更一般的文件,那么你可以開始觀察分析器的輸出對稱加密符號。在這里,你只需要確保你的CPU有AES-NI支持,并且您設置了服務器端的喜好AES-GCM密碼。適當調(diào)整硬件應該有以下perf top:
不僅是你的服務器需要處理的加密/解密,客戶端也能夠減小CPU消耗。如果沒有硬件加速,這可能是非常具有挑戰(zhàn)性的,因此,你可以考慮使用被設計成無需硬件加速快的算法,如ChaCha20-Poly1305。
ChaCha20-Poly1305在BoringSSL支持開箱即用,使用OpenSSL 1.0.2的話可以考慮使用CloudFlare的補丁。BoringSSL還支持“ 具有相同的優(yōu)先密碼組 ”,所以您可以使用下面的配置,讓客戶決定根據(jù)自己的硬件功能決定:
應用程序級別:高層
要分析該級別的優(yōu)化效果,你需要收集 RUM 數(shù)據(jù)。在瀏覽器中,你可以使用 Navigation Timing APIs 和 Resource Timing APIs。你的主要指標 TTFB 和 TTV / TTI。具有易于可查詢和 graphable 格式將大大簡化重復數(shù)據(jù)。
壓縮
nginx 的壓縮開始于 mime.types 文件,它定義文件擴展名和 MIME 類型之間默認的對應關系。然后,你需要定義什么類型使用 gzip_types。如果你想完整的列表,你可以使用 MIME-DB 到自動生成的 mime.types,并與添加這些 .compressible == true 到 gzip_types。
當啟用 gzip,必須注意的是兩個方面:
-
增加內(nèi)存使用情況。這可以通過限制來解決 gzip_buffers。
增加 TTFB 緩沖。這可以通過 gzip_no_buffer 來解決。
HTTP 壓縮不限于只 gzip 壓縮:nginx 具有第三方 ngx_brotli 模塊,較 gzip 的壓縮比可以提高 30%。
至于壓縮設置本身,讓我們討論兩個獨立的用例:靜態(tài)和動態(tài)數(shù)據(jù)。
-
對于靜態(tài)數(shù)據(jù),可以通過預壓縮靜態(tài)資產(chǎn)作為構建過程的一部分來歸檔最大壓縮比。
對于動態(tài)數(shù)據(jù),你需要仔細權衡一個完整的往返:時間壓縮數(shù)據(jù) 時間以進行傳輸 時間在客戶端上解壓縮。因此,在設置盡可能高的壓縮級別可能是不明智的,不僅是從CPU使用率的角度來看,而且要從TTFB一起看。
代理中的緩沖區(qū)可以極大地影響 Web 服務器的性能,特別是相對于延遲。nginx 的代理模塊具有多種緩沖開關。您可以分別控制緩沖 proxy_request_buffering 和 proxy_buffering。如果想啟用緩沖存儲器上消耗上限是設置 client_body_buffer_size 和proxy_buffers 這兩個選項。為了響應這可以通過設置被禁用 proxy_max_temp_file_size 為 0。
緩沖最常用的方法有:
-
緩沖器請求/響應達到內(nèi)存中的某個閾值之后,溢出到磁盤。如果請求啟用了請求緩沖,您只在它完全接收后向后端發(fā)送一個請求,并且通過響應緩沖,一旦響應準備就緒,就可以立即釋放后端線程。這種方法可以提高吞吐量但是會增加響應時間。
無緩沖。緩沖可能不是對延遲敏感路由的一個好選擇,尤其是使用流媒體的情況。對于他們,你可能想禁用它,但現(xiàn)在你的后端需要處理慢客戶機(包括惡意慢/慢讀的攻擊)。
應用通過控制響應緩沖X-Accel-Buffering報頭。
無論您選擇何種方式,不要忘記測試其在TTFB和TTLB效果。此外,如前面提到的,緩沖會影響IO使用情況,甚至后端的利用率,所以要留意這一點。
TLS
現(xiàn)在我們要談論TLS和改善延遲,高層次的方面,可以通過正確配置nginx的完成。大部分優(yōu)化都在nginx conf 2014的 High Performance Browser Networking’s “Optimizing for TLS” 中談到了,如果不確定,請咨詢Mozilla的服務器端TLS指南和/或您的安全團隊。
為了驗證你可以使用優(yōu)化的結果:
-
WebpageTest 對性能的影響。
SSL 服務器測試從 Qualys 公司,或 Mozilla TLS 對安全的影響。
會話恢復
作為數(shù)據(jù)庫管理員愛說“最快的查詢是你什么也不查”,同樣,TLS 可以緩存的握手結果從而減少一個 RTT。有這樣做的方法有兩種:
-
你可以要求客戶端存儲所有會話參數(shù)(簽名和加密方式),在下一次握手(類似于一個cookie)時將其發(fā)送給您。在nginx這邊,通過配置ssl_session_tickets指令。這不不會消耗在服務器端的任何內(nèi)存,但是有一些缺點的:
你需要的基礎設施來創(chuàng)建,旋轉(zhuǎn),并為您的TLS會話分配隨機加密/簽名密鑰。只要記住,你真的不應該1)使用源代碼控制存儲票鍵2)生成其它非短暫的物質(zhì),如日期或證書這些密鑰。
PFS不會在每個會話的基礎,但每個TLS票鍵的基礎上,因此,如果攻擊者獲取票據(jù)密鑰的時候,就可以潛在地解密任何捕獲流量票的時間。
你的密碼將被限制在您的標簽密鑰的大小。如果您正在使用128位的標簽密鑰則沒有多大意義,推薦使用AES256。Nginx的同時支持128位和256位的TLS票鍵。
并非所有的客戶端都支持。
或者你也可以存儲服務器上的TLS會話參數(shù),只給出一個引用(一個ID)給客戶端。這是通過完成ssl_session_cache指令。它有助于在會話之間保持PFS和大大限制攻擊面。雖然門票鍵有缺點:
在服務器上,每個會話消耗256字節(jié)的內(nèi)存,這意味著您不能存儲過多的數(shù)據(jù)。
他們不能在服務器之間輕松共享。因此您可能需要負載均衡器,這需要在同一客戶端發(fā)送到同一臺服務器保存緩存位置,或者使用ngx_http_lua_module寫一個分布式TLS會話存儲。
作為一個側面說明,如果你用會話票據(jù)的辦法,使用 3 個選項:
你將永遠與當前的密鑰加密,但接受與未來都和以前的密鑰加密的會話。
OCSP Stapling
你應該縮短您 OCSP 響應,否則的話:
-
TLS握手可能需要更長的時間,因為客戶將需要聯(lián)系認證機構來獲取OCSP狀態(tài)。
OCSP的故障可能導致可用性降低。
你可能會損害用戶的隱私,因為他們的瀏覽器將與第三方服務聯(lián)系。
OCSP 響應您可以定期從你的證書頒發(fā)機構獲取它,結果分發(fā)到您的網(wǎng)絡服務器,并與使用它 ssl_stapling_file 的指令:
TLS 記錄大小
TLS數(shù)據(jù)分解成塊稱為記錄,在您完全接收它之前,無法對其進行驗證和解密。
默認情況下nginx的使用16K塊,甚至不適合IW10擁塞窗口,因此需要額外的往返。nginx提供了一種通過設置記錄大小ssl_buffer_size指令:
-
為了優(yōu)化低延遲,你應該把它設置為小塊,比如4K,從CPU使用的角度來看,進異步減少會更昂貴。
為了優(yōu)化高通量應設置在16K。
有兩個問題:
-
您需要手動調(diào)整它。
您只能在每個 nginx 配置或每服務器塊基礎上設置 ssl_buffer_size, 因此, 如果您有一個具有混合延遲/吞吐量工作負載的服務器就搞不定了。
還有另一種方法:動態(tài)記錄大小調(diào)整。有一個從CloudFlare的Nginx補丁,增加了對動態(tài)記錄大小的支持。最初的配置比較痛苦,一旦就緒就一勞永逸。
TLS 1.3
TLS 1.3 功能的確聽起來很不錯,但除非你有資源,否則我建議不啟用它,因為:
-
它仍然是一個草案。
0-RTT 握手有一定的安全隱患。你的應用程序需要做好準備。
還有中間件(防病毒軟件,干粉吸入器等),阻止未知 TLS 版本。
Nginx的是基于事件循環(huán)的Web服務器,這意味著它在同一時間內(nèi)只能做一件事情。盡管似乎它所有的這些事情同時,所有nginx的不只是快速的事件之間切換。它所有的工作,因為處理每個事件只需要幾微秒。但是如果它開已經(jīng)占用了太多的時間,延遲可能扶搖直上。
如果你開始注意到你的nginx花費太多的時間內(nèi)ngx_process_events_and_timers功能,并且分布是雙峰的,那么你很可能是由事件循環(huán)攤位的影響。
AIO 和線程池
由于事件循環(huán)攤位特別是在旋轉(zhuǎn)磁盤的主要來源是 IO,你應該檢查這里。通過運行 fileslower
可以測量你多少受到它的影響:
為了解決這個問題,Nginx 已經(jīng)卸載的 IO 線程池的支持(也有支持 AIO,但 Unix 本地 AIO 有很多怪癖,所以最好避免它,除非你知道你在做什么)。基本的設置包括簡單的:
aio threads;
aio_write on;
對于更復雜的情況下,您可以設置自定義thread_pool
的,如果一個磁盤出故障不會影響請求的其余部分。線程池可以大大減少nginx進程數(shù)困在D狀態(tài),改善延遲和吞吐量。但這并不能消除eventloop的問題。
日志寫日志也可能會導致長時間卡頓,因為這也是在做磁盤操作。您可以通過運行ext4slower來檢查
,并尋求獲得/錯誤日志引用:
這是可能通過使用寫他們之前在內(nèi)存中后臺訪問日志來解決此 buffer
參數(shù)的access_log
指令。通過使用gzip
參數(shù),您也可以將它們寫入磁盤,寫入磁盤前壓縮也可以減少IO壓力。
但要完全消除日志 IO 影響,寫你應該通過系統(tǒng)日志,這樣日志將被完全nginx的事件循環(huán)集成。
打開文件緩存
由于 open(2) 調(diào)用本質(zhì)上是
阻塞的,Web 服務器通常打開/讀取/關閉文件,因此打開文件的緩存可能是有益的。通過ngx_open_cached_file 可以看到效果
:
如果你能看到太多 open 調(diào)用或有一些 open 調(diào)用花費太多時間,你可以可以考慮打開 open_file_cache:
啟用后 open_file_cache
,你可以通過觀察所有的高速緩存未命中opensnoop
,并決定是否需要調(diào)整高速緩存限制:
小結
文中所描述的所有優(yōu)化都是單個 Web 服務器框的優(yōu)化。他們中的一些可以提高可擴展性和性能。如果您希望以最小的延遲服務請求,或者更快地向客戶機發(fā)送字節(jié),其他服務也適用。但在我們的經(jīng)驗中,用戶可見的大量性能來自于一個更高級別的優(yōu)化,它影響了整個 Dropbox 邊緣網(wǎng)絡的行為,如入口/出口流量工程和更聰明的內(nèi)部負載平衡。這些問題是知識的邊緣,而工業(yè)才剛剛開始接近它們。