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