乳尖春药H糙汉共妻,国产成人无码精品亚洲,少妇肉欲系列1000篇,免费永久看黄神器

公司新聞

如何優化Web服務器以實現高吞吐量和低延遲

作者:admin 日期:2023-09-03 瀏覽:

如何優化Web服務器以實現高吞吐量和低延遲

  點擊上方“CSDN”,選擇“置頂公眾號”

  關鍵時刻,第一時間送達!

  作者丨Alexey Ivanov

  翻譯丨不二

  譯者注:人們更多的是關注軟件一類的優化,當負載上來后,發現硬件發揮不出最大性能。服務器出廠時,BIOS 默認達到了性能和能耗之間的良好平衡,以適應一般環境,然而在不同的環境下可能需要對服務器進行優化,以獲得最大的吞吐量或最低的延遲,本文全面講述如何在硬件層面優化web服務器,請看譯文。

  這是對2017年9月6日在NginxConf 2017年演講內容的延伸。作為Dropbox Traffice團隊的網絡可靠性工程師(SRE,Site Reliability Engineer ),我負責公司的邊緣網絡,包括它的可靠性、性能和效率。Dropbox 邊緣網絡是一個基于nginx的代理層,用于處理敏感的元數據事務和高吞吐量數據傳輸。在處理數以萬計敏感事務的系統中,通過TCP/IP協議和內核,從驅動程序、中斷到庫、應用級調優,整個代理堆棧中都存在效率/性能優化。

  免責聲明

  這篇文章將討論如何調優web服務器和代理服務器,并采用科學的方法,將它們逐一應用測量效果,最終確定是否真的對所處的環境有用。

  這不是一篇關于Linux性能的文章,盡管它大量引用了bcc工具、eBPF和perf,但不要誤以為是使用性能分析工具的全面指南。如果你想了解更多關于他們的信息,可以通過Brendan Gregg的博客閱讀。

  這也不是一篇關于瀏覽器性能的文章。當介紹與延遲相關的優化時,會涉及到客戶端的性能,但只是短暫的。如果你想知道更多這方面的,可以閱讀Ilya Grigorik的高性能瀏覽器網絡。

  最后,這也不是關于TLS(傳輸層安全協議)的最佳實踐編譯,盡管會提到TLS庫及其設置,但那是為了評估每種方法的性能和安全性。可以使用Qualys SSL測試,以驗證網絡終端與當前的最佳實踐,如果想了解更多關于TLS的信息,請考慮閱讀Feisty Duck Bulletproof TLS Newsletter。

  文章的結構

  文章將討論系統不同層級的效率/性能優化。從硬件和驅動程序的最低級別開始:這些調優可以應用到幾乎任何高負載服務器上。然后將轉移到linux內核及其TCP/IP協議棧:這些是在任何一個TCP-heavy盒子上嘗試的旋鈕。最后將討論庫和應用程序級別的調優,它們主要適用于一般的web服務器和nginx服務器。

  對于每一個潛在的優化領域,將嘗試提供一些關于延遲/吞吐量權衡(如果有的話)的背景知識,以及監控指導方針,最后建議對不同的工作負載作調整。

  硬件方面

  CPU

  對于良好的非對稱RSA/EC性能,具有至少AVX2(AVX2 in /proc/ cpuinfo)支持的處理器,最好是具有大型整數算術能力硬件(bmi和adx)的處理器。而對于對稱的情況,應該找AES ciphers和AVX512的ChaCha+Poly。英特爾通過OpenSSL 1.0.2對不同的硬件產品進行了性能比較,說明了這些硬件卸載的影響。

  CPU延遲敏感的用例,如路由選擇,減少NUMA的節點和禁用HT會有幫助。擁有更多內核意味著高吞吐量的任務會處理得更好,并且將從超線程(除非它們是緩存綁定)中受益,而且通常不會過多地關心NUMA。

  具體地說,如果走英特爾的路線,至少需要haswell或broadwell架構的處理器與合適的Skylake CPUs。如果采用AMD,那么EPYC相當不錯。

  NIC(網絡適配器)

  至少需要10G網卡,最好是25G。如果想要通過TLS單個服務器達到更大的傳輸,這里所描述的調優是不夠的,可能需要將TLS構建到內核級別(例如FreeBSD,Linux)。

  在軟件方面,應該尋找具有活動郵件列表和用戶社區的開源驅動程序。如果涉及到調試與驅動相關的問題,這將是非常重要的。

  內存

  選擇的經驗法則是,延遲敏感的任務需要更快的內存,而吞吐量敏感的任務需要更多的內存。

  硬盤

  這取決于緩沖/緩存的需求,如果要緩存大量的內容,應該選擇基于flash的存儲。有些甚至采用了專門的flash友好文件系統(通常是日志結構的),但是它們并不總是比普通的ext4/xfs性能更好。

  無論如何,注意不要因為忘記啟用TRIM或更新固件而燒穿了flash。

  操作系統:低水平

  固件

  保持固件最新,以避免痛苦和冗長的故障排除會話。盡量保持最新的CPU微碼,主板,NICs和ssd固件。這并不意味著要一直花錢,經驗法則是把固件更新到上一個版本就行,除非它在最新版本中修復了關鍵錯誤,否則只要不要太落后就行。

  驅動

  更新規則和固件更新差不多,嘗試保持最新。這里需要注意的是,如果可能的話,嘗試將內核升級與驅動程序更新解耦。例如,可以用DKMS打包驅動程序,或者為使用的所有內核版本預編譯驅動程序。這樣當更新內核出現故障時不需要考慮驅動程序。

  CPU

  在Ubuntu/Debian中,可以使用一些實用工具安裝linux工具包,但是現在只使用cpupower、turbostat和x86_energy_perf_policy即可。為了驗證cpu相關的優化,可以通過常用的負載生成工具對軟件進行壓力測試(例如,Yandex使用Yandex.Tank)。下面是有一份來自NginxConf開發者的演示,介紹了nginx加載測試的最佳實踐:“nginx性能測試”。

  cpupower

  使用這個工具比crawling /proc/更容易。要查看有關處理器及其頻率調控器的信息,請運行以下代碼。

  $ cpupower frequency-info

  ...

  driver: intel_pstate

  ...

  available cpufreq governors: performance powersave

  ...

  The governor "performance" may decide which speed to use

  ...

  boost state support:

  Supported: yes

  Active: yes

  檢查是否啟用了Turbo Boost,對于Intel cpu確保運行的是intel_pstate,而不是acpi-cpufreq或者pcc-cpufreq。如果堅持使用acpic-cpufreq,那么應該升級內核,或者如果不能升級,確保使用的是performance調控器。在使用intel_pstate運行時,powersave調控器也應該運行良好,但這一步需要自己去驗證。

  想看看空載時CPU到底發生了什么,可以使用turbostat直接查看處理器的MSRs,獲取電源、頻率和空閑狀態信息。

  # turbostat --debug -P

  ... Avg_MHz Busy% ... CPU%c1 CPU%c3 CPU%c6 ... Pkg%pc2 Pkg%pc3 Pkg%pc6 ...

  這里可以看到實際的CPU頻率(是的,/proc/cpuinfo欺騙了你),以及核心/包空閑狀態。

  如果使用intel_pstate驅動程序,CPU的空閑時間會比預想的要多,可以采取以下措施。

  設置性能調節。

  設置x86_energy_perf_policy性能。

  或者,對于非常延遲的關鍵任務,還可以這樣做。

  采用/dev/cpu_dma_latency接口。

  對于UDP流量,使用busy-polling方法。

  更多關于處理器電源管理的信息,可以在2015年的LinuxCon Europe上由“Linux內核”的Intel開源技術中心發表的“Linux內核中的平衡能力和性能”文章中了解到。

  CPU親和力

  還可以通過在每個線程/流程上應用CPU關聯性來降低延遲,例如nginx,它具有worker_cpu_affinity指令,可以自動將每個web服務器進程綁定到它自己的核心。這可以消除CPU遷移,減少緩存遺漏和頁面錯誤,并稍微增加每個周期的指令數。所有這些都可以通過perf stat來驗證。

  遺憾的是,啟用親和性也會增加等待空閑CPU的時間,從而對性能產生負面影響。可以通過在pid上運行runqlat來監控。

  usecs : count distribution

  0 -> 1 : 819 | |

  2 -> 3 : 58888 |****************************** |

  4 -> 7 : 77984 |****************************************|

  8 -> 15 : 10529 |***** |

  16 -> 31 : 4853 |** |

  ...

  4096 -> 8191 : 34 | |

  8192 -> 16383 : 39 | |

  16384 -> 32767 : 17 | |

  如果看到多毫秒的延遲,那么除了nginx本身之外,服務器可能在處理其他很多的事,一旦關聯將增加延遲,而不是減少。

  內存

  所有的mm/tunings通常都是非常具體的工作流,可以推薦的方法很少。

  確定有所幫助的話,設置THP并進行啟用,否則會適得其反得到一個數量級的減速,而不是20%的目標延遲改進。

  針對單個NUMA節點設置vm.zone_reclaim_mode為0.## NUMA。

  現代的CPU實際上包含多個獨立的CPU,它們通過高速互連和共享資源,從HT核心的L1緩存開始,經過包內的L3高速緩存,再到內存和套接字內PCIe鏈路。這基本上就組成了NUMA,它具有快速互連的多個執行和存儲單元。

  對于NUMA的全面概述及其影響,可以參考Frank Denneman的“NUMA深入分析系列”。

  對于NUMA長話短說,可以進行以下選擇:

  選擇忽略,在BIOS中禁用它,或者在numactl –interleave=all下運行軟件,可以得到一般的性能。

  通過使用單節點服務器來替換它,就像Facebook和OCP Yosemite平臺一樣。

  選擇接受,并優化用戶和內核空間中的CPU/內存。

  現在討論第三種選擇,因為前兩種方法并沒有太多的優化。

  要合理地使用NUMA需要將每個NUMA節點視為一個單獨的服務器,對此應該首先檢查拓撲結構,可以通過numactl –hardware查看。

  考慮的因素有:

  節點的數量。

  每個節點的內存大小。

  每個節點的cpu數量。

  節點之間的距離。

  這是一個非常糟糕的用例,因為它有4個節點同時沒有附加內存的節點。在不犧牲系統一半內核的情況下,很難把每個節點都當作獨立的服務器來處理。

  可以通過使用numastat來驗證:

  也可以以/proc/meminfo格式要求numastat輸出每個節點的內存使用統計信息。

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

  $ numactl --hardware

  available: 2 nodes (0-1)

  node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23

  node 0 size: 46967 MB

  node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31

  node 1 size: 48355 MB

  由于節點基本是對稱的,可以通過numactl –cpunodebind=X –membind=X將應用程序的實例綁定到對應的NUMA節點,然后在另一個端口上公開它,這樣就可以通過使用兩個節點獲得更好的吞吐量,通過保留內存位置獲取更好的延遲。

  通過對內存操作的延遲來驗證NUMA的放置效率,例如使用bcc的funclatency來度量內存重操作的延遲,像memmove。

  在內核方面,使用perf stat來觀察效率,并查詢相應的內存和調度器事件。

  對network-heavy工作負載,最后一點與numa相關的優化來自于一個事實,即網絡適配器是一個PCIe設備,每個設備都綁定到自己的numa節點,因此在與網絡通信時,一些cpu的延遲時間會較低。討論NIC和CPU親和力時將涉及到優化可以應用到哪些地方,現在先說一說PCIe。

  PCIe

  通常情況下,除非出現某種硬件故障,否則不需要深入地進行PCIe故障排除。因此只需創建“鏈接寬度”、“鏈接速度”,并盡可能為PCIe設備創建RxErr/BadTLP提醒。當硬件損壞或PCIe協商失敗,這將節省故障排除時間。可以使用lspci達到目的。

  PCIe可能會成為一個瓶頸,如果存在多個高速設備競爭帶寬(例如將快速網絡與快速存儲結合起來),那么可能需要在cpu之間物理地切分PCI總線設備以獲得最大的吞吐量。

  圖片來源:https://en.wikipedia.org/wiki/PCI_Express History_and_revisions。

  Mellanox網站上有篇文章“理解PCIe配置的最大性能”,更深入的介紹了PCIe配置,對于高速傳輸中出現卡頓和操作系統之間的數據包丟失會有所幫助。

  英特爾公司表示,有時PCIe的權力管理(ASPM)可能導致更高的延遲,從而導致更高的包損失,但可以通過向內核cmdline添加pcie_aspm=off禁用它。

  NIC

  開始之前需要說明的是,Intel和Mellanox都有自己的性能調優指南,無論選擇哪種供應商,都有助于了解它們。同時,驅動程序通常也有自己的README和一套實用工具。

  下一個需要閱讀的是操作系統的手冊,例如紅帽企業版Linux系統網絡性能調優指南,它提到了下面的大多數優化,甚至更多。

  Cloudflare也提供了一篇關于在博客上調優網絡堆棧的一部分的文章,盡管它主要是針對低延遲的用例。

  在優化網絡適配器時,ethtool將提供很好的幫助。

  注意,如果正在使用一個最新的內核,應該在userland中添加一些部分,比如對于網絡操作,可能需要最新的:ethtool,iproute2,以及iptables/nftables包。

  可以通過ethtool -s了解網卡信息。

  $ ethtool -S eth0 | egrep 'miss|over|drop|lost|fifo'

  rx_dropped: 0

  tx_dropped: 0

  port.rx_dropped: 0

  port.tx_dropped_link_down: 0

  port.rx_oversize: 0

  port.arq_overflows: 0

  與NIC制造商進行詳細的統計描述,例如Mellanox為他們提供了一個專門的wiki頁面。

  內核方面將看到/proc/interrupts、/proc/softirqs和/proc/net/softnet_stat。這里有兩個有用的bcc工具,hardirqs和softirqs。優化網絡的目標是在沒有數據包丟失的情況下,對系統進行優化,直至達到最小的CPU使用量。

  中斷親和力

  調優通常從處理器之間的擴展中斷開始,具體怎么做取決于工作量。

  為了獲取最大吞吐量,可以在系統中的所有numa節點上分發中斷。

  為了得到最低延遲,可以將中斷限制為單個numa節點。要做到這一點,可能需要減少隊列的數量,以適應單個節點(這通常意味著用ethtool -L將它們的數量減少一半)。

  供應商通常提供腳本以達到目的,例如英特爾有set_irq_affinity。

  環形緩沖區大小

  網卡與內核交換信息一般是通過一種叫做“環”的數據結構來完成的,該數據結構借助ethtool -g可以查看該“環”的當前/最大尺寸。

  $ ethtool -g eth0

  Ring parameters for eth0:

  Pre-set maximums:

  RX: 4096

  TX: 4096

  Current hardware settings:

  RX: 4096

  TX: 4096

  可以在預先設置的最大值與-G之間作調整,一般來說越大越好(特別是如果使用中斷合并時),因為它將給予更多的保護來防止突發事件和內核內的間斷,因此降低了由于沒有緩沖區空間/錯過中斷而減少的數據包數量。但有幾點需要說明:

  在舊的內核,或者沒有BQL支持的驅動程序中,設置很高的值意味著tx-side更高的緩存過滿。

  更大的緩沖區也會增加緩存的壓力。

  合并

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

  $ ethtool -c eth0

  Coalesce parameters for eth0:

  ...

  rx-usecs: 50

  tx-usecs: 50

  可以使用靜態限制嚴格限制每秒中斷的最大中斷數,或者依賴硬件基于吞吐量自動調整中斷率。

  啟用合并(使用 -c)將增加延遲,并可能引發包丟失,所以需要避免它對延遲敏感。另一方面,完全禁用它可能導致中斷節流,從而影響性能。

  卸載

  現代網卡比較智能,可以將大量的工作轉移到硬件或仿真驅動程序上。

  所有可能的卸載都可以通過ethtool -k查看。

  $ ethtool -k eth0

  Features for eth0:

  ...

  tcp-segmentation-offload: on

  generic-segmentation-offload: on

  generic-receive-offload: on

  large-receive-offload: off [fixed]

  在輸出中,所有不可調的卸載都采用[fixed]后綴標記。

  關于它們有很多討論,但這里有一些經驗法則。

  不要啟用LRO,使用GRO代替。

  要小心使用TSO,因為它高度依賴于驅動/固件的質量。

  不要在舊的內核中啟用TSO/GSO,因為它可能會導致嚴重的緩存過滿。所有現代NICs都優化了多核硬件,因此他們將包內部拆分為虛擬隊列(通常是一個CPU)。當它在硬件中完成時,它被稱為RSS,當操作系統負責交叉cpu的負載平衡包時,它被稱為RPS(與TX-counterpart對應的稱為XPS)。當操作系統試圖變得智能并路由到當前正在處理該套接字的cpu時,它被稱為RFS。當硬件實現該功能時,它被稱為“加速RFS”或簡稱“aRFS”。

  以下是生產中的一些最佳實踐。

  如果正在使用最新的25 G+硬件,它可能有足夠的隊列和一個巨大的間接表,以便能夠在所有的內核中提供RSS。一些老的NICs只使用前16個cpu。

  以下情況可以嘗試啟用RPS:

  當有更多的cpu,而不是硬件隊列,希望為吞吐量犧牲延遲。

  當使用的是內部隧道(如GRE / IPinIP),NIC不能RSS。

  如果CPU相當舊且沒有x2APIC,則不要啟用RPS。

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

  RFS的有效性很大程度上取決于工作負載,以及是否將CPU的相關性應用于它。

  導流器和ATR

  啟用導流器(或英特爾術語中的fdir)在應用程序的目標路由模式中是默認操作,該應用程序通過取樣包和轉向系統來實現aRFS,并將其控制在可能被處理的核心位置。它的統計數據也可以通過ethtool - s:$ ethtool - s eth0 |egrep “fdir” port.fdir_flush_cnt:0實現。

  盡管英特爾聲稱fdir在某些情況下提高了性能,但外部研究表明它也引發了最多1%的包重新排序,這對TCP性能有很大的損害。因此可以測試一下,看看FD是否對工作負載有幫助,同時要注意TCPOFOQueue計數器。

  操作系統:網絡棧

  對于Linux網絡堆棧的調優,有無數的書籍、視頻和教程。可悲的是,大量的“sysctl.conf cargo-culting”隨之而來,即使最近的內核版本不需要像10年前那樣多的調優,而且大多數新的TCP/IP特性在默認情況下都是啟用和已調優的,人們仍然在復制舊的已經使用了2.6.18/2.6.32內核的sysctls.conf。

  為了驗證與網絡相關的優化效果,需要:

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

  合并從ss -n –extended –info,或者從網頁服務器內部調用getsockopt(TCP_INFO)/getsockopt(TCP_CC_INFO)獲取的每個連接的指標。

  采樣TCP流的tcptrace(1)。

  從應用程序/瀏覽器中分析RUM指標。

  對于關于網絡優化的信息來源,個人喜歡由CDN-folks進行的會議討論,因為他們通常知道在做什么,比如LinuxCon在澳大利亞的快速發展。聽Linux內核開發人員關于網絡的說法也是很有啟發性的,例如netdevconf對話和NETCONF記錄。

  通過PackageCloud深入到Linux網絡堆棧中時以下需要注意,特別是當側重監視而不是盲目的調優時。

  監視和調優Linux網絡堆棧:接收數據

  監視和調優Linux網絡堆棧:發送數據

  開始之前再次重申:請升級內核!有大量的新網絡堆棧改進,比如關于新的熱像:TSO自動調整,FQ,步測,TLP,和機架,但以后更多。通過升級到一個新的內核,將得到一大堆拓展性改進,如刪除路由緩存、lockless偵聽套接字、SO_REUSEPORT等等。

  概述

  最近的Linux網絡文章中非常顯目的是“讓Linux TCP快速運行”。它通過將Linux sender-side TCP堆棧分解為功能塊,從而鞏固了Linux內核的多年改進。

  公平隊列和pacing

  公平隊列負責改善TCP流之間的公平性和減少行阻塞,這對包的下降率有積極的影響。以擁塞控制的速度設定數據包,以同樣的間隔時間間隔來設置數據包,從而進一步減少數據包損失,最終提高吞吐量。

  附注一點:在linux中,通過fq qdisc可以獲得公平隊列和pacing。不要誤以為這些是BBR的要求,它們都可以與CUBIC一同使用以減少15-20%的數據包損失,從而提高CCs上損失的吞吐量。只是不要在舊的內核(低于3.19版本)中使用它,因為會結束pacing pure ACKs,并破壞uploads/RPCs。

  TSO autosizing和TSQ

  兩種方法都負責限制TCP堆棧中的緩沖,從而降低延遲,且不會犧牲吞吐量。

  擁塞控制

  CC算法本身設計領域很廣,近年來圍繞它們進行了大量的活動。其中一些活動被編纂為:tcp_cdg(CAIA)、tcp_nv(Facebook)和tcp_bbr(谷歌)。這里不會太深入地討論他們的內部工作,先假設所有的人都依賴于延遲的增加而不是數據包的減少。

  BBR可以說是所有新的擁塞控制中,文檔完整、可測試和實用的。基本思想是建立一個基于包傳輸速率的網絡路徑模型,然后執行控制循環,最大限度地提高帶寬,同時最小化rtt。這正是代理堆棧中需要的。

  來自BBR實驗的初步數據顯示,文件下載速度有提升。

  在東京的BBR實驗6小時:x軸表示時間,y軸表示客戶端下載速度。

  需要強調的是有觀察到所有百分位數的速度都在增長,這不是后端修改引起。通常只會給p90+用戶帶來好處(互聯網連接速度最快的用戶),因為其他所有人都是帶寬限制的。網絡級的調優,比如改變擁塞控制,或者啟用FQ /pacing顯示用戶沒有帶寬限制,但是他們是“tcp - limited”。

  如果想了解更多關于BBR的信息,APNIC有一個很好的入門級的BBR概述(它與基于損失的擁擠控制的對比)。關于BBR的更深入的信息,可以閱讀bbr-dev郵件列表檔案(它在頂部有許多有用的鏈接)。對擁塞控制感興趣的,可以關注網絡擁塞控制研究小組的有趣活動。

  ACK處理和丟失檢測

  關于擁塞控制已經說了很多,下面再次運行最新的內核討論一下丟失檢測。那些新的啟發式方法,比如TLP和RACK,經常被添加到TCP,而FACK和ER等舊的東西正在被淘汰。一旦添加,它們是默認啟用的,因此在升級之后不需要調整任何系統設置。

  用戶空間優先級和HOL

  用戶空間套接字api提供隱式緩沖,一旦被發送,就無法重新排序塊,因此在多路復用場景中(例如HTTP/2),可能導致HOL阻塞,以及h2優先級反轉。TCP_NOTSENT_LOWAT套接字選項(和相應的net.ipv4.tcp_notsent_lowat sysctl)被設計用來解決這個問題,它設置了一個閾值,在這個閾值中,套接字會認為自己是可寫的(即epoll會對欺騙應用程序)。從而可以解決使用HTTP/2優先級的問題,但它也可能對吞吐量產生負面影響。

  Sysctls

  在談到網絡優化的時候,不能不提到關于sysctls的調優。先從不熟悉的開始。

  net.ipv4.tcp_tw_recycle=1——不要使用它——它已經被NAT的用戶破壞了,但是如果升級內核將有所改善。

  net.ipv4.tcp_timestamps=0——不要禁用它們,除非已知所有的副作用,而且可以接受它們。例如,一個不明顯的副作用是可以在syncookie上打開窗口縮放和SACK選項。

  對于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較早的開始。

  同樣值得注意的是,有一份由curl的作者Daniel Stenberg編寫的RFC草案,命名為HTTP的TCP調優,它試圖整理所有可能對HTTP有利的系統調優。

  應用程序級別:中層

  工具

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

  一旦有了新的工具,就可以適當地調整和觀察系統的行為。這一部分主要依賴于通過perf top、on-CPU flamegraphs和來自bcc funclatency的臨時直方圖進行on-cpu分析。

  編譯器工具鏈

  如果想要編譯硬件優化的組件,那么擁有一個現代的編譯器工具鏈是必不可少的,這在許多web服務器通常使用的庫中也是存在的。

  除了性能之外,新的編譯器具有新的安全特性(如-fstack-protector-strong或SafeStack),這些特性是想在邊緣網絡中應用的。現代工具鏈的另一個用例是,當運行測試工具時,殺毒軟件(例如AddressSanitizer和friends)編譯的是二進制文件。

  系統庫

  推薦升級系統庫,比如glibc,因為在其他方面,可能會錯過-lc、-lm、-lrt等低級功能中最近存在的優化。

  Zlib

  通常,web服務器將負責壓縮。根據多少數據將通過代理,偶爾會在perf top看到zlib的符號,例如:

  # perf top

  ...

  8.88% nginx [.] longest_match

  8.29% nginx [.] deflate_slow

  1.90% nginx [.] compress_block

  在最低級別上有一些優化的方法:英特爾和Cloudflare,以及一個獨立的zlib-ng項目,都有他們的zlib forks,可以通過使用新的指令集提供更好的性能。

  Malloc

  在討論優化之前,我們主要是面向cpu的,但是現在換一個角度,討論與內存相關的優化。如果使用大量的Lua和FFI或第三方的重模塊來執行它們自己的內存管理,可能會由于碎片化而增加內存使用。可以嘗試通過切換到jemalloc或tcmalloc來解決這個問題。

  使用自定義malloc有以下好處。

  將nginx二進制文件與環境分離,glibc版本升級和操作系統遷移會減少它的影響。

  更好的自查、分析和統計。

  PCRE

  如果在nginx configs中使用許多復雜的正則表達式,或者嚴重依賴Lua,perf top會顯示與pcre相關的標志。可以通過使用JIT編譯PCRE進行優化,也可以通過pcre_jit在nginx中啟用它。

  通過查看火焰圖或者使用funclatency檢查優化的結果。

  # funclatency /srv/nginx-bazel/sbin/nginx:ngx_http_regex_exec -u

  ...

  usecs : count distribution

  0 -> 1 : 1159 |********** |

  2 -> 3 : 4468 |****************************************|

  4 -> 7 : 622 |***** |

  8 -> 15 : 610 |***** |

  16 -> 31 : 209 |* |

  32 -> 63 : 91 | |

  TLS

  如果在CDN前端的w/o邊緣終止TLS,那么TLS性能優化的價值是非常可觀的。在討論調優時主要關注服務器端效率。

  因此需要決定的第一件事是使用哪些TLS庫:Vanilla OpenSSL、OpenBSD LibreSSL或谷歌的BoringSSL。在選擇TLS庫的偏好之后,需要適當地構建它,例如,OpenSSL有一堆構建時的啟發式,可以根據構建環境進行優化;BoringSSL具有確定性的構建,但遺憾的是它更保守,并且在默認情況下禁用了一些優化。無論如何,在這里優先選擇現代的CPU,大多數TLS庫可以使用從AES-NI和SSE到ADX和AVX512的所有屬性。可以使用帶有TLS庫的內置性能測試,例如在BoringSSL案例中,內置有bssl speed。

  大多數性能不是來自已有的硬件,而是來自將要使用的cipher - suite,因此必須仔細地優化它們。也需要知道這里的變化會影響web服務器的安全性——最快的密碼套件不一定是最好的。如果不確定要使用什么加密設置,Mozilla SSL配置生成器是一個很好的選擇。

  非對稱加密

  如果服務處于邊緣,那么可能會觀察到相當數量的TLS握手,因此CPU占用了大量的非對稱密碼,這使它成為優化的明顯目標。

  為了優化服務器端CPU,可以切換到ECDSA certs,其速度通常比RSA快10倍,而且它們的體積要小得多,因此在出現包丟失時能加速握手。但是ECDSA也嚴重依賴于系統的隨機數生成器的質量,所以如果使用的是OpenSSL,那么一定要有足夠的熵值(使用BoringSSL,不必擔心這一點)。

  附注一點,越大并不總是越好,例如使用4096個RSA證書會降低10倍的性能。

  $ bssl speed

  Did 1517 RSA 2048 signing ... (1507.3 ops/sec)

  Did 160 RSA 4096 signing ... (153.4 ops/sec)

  更糟糕的是,小的也不一定是最好的選擇:使用non-common p-224字段與更常見的p-256相比,會降低60%的性。

  $ bssl speed

  Did 7056 ECDSA P-224 signing ... (6831.1 ops/sec)

  Did 17000 ECDSA P-256 signing ... (16885.3 ops/sec)

  原則上最常用的加密通常是最優的加密。

  在使用RSA certs運行適當優化的基于opentls的庫時,可以在perf top:AVX2-capable看到以下的跟蹤信息,但不能使用ADX-capable盒子(例如Haswell)。

  6.42% nginx [.] rsaz_1024_sqr_avx2

  1.61% nginx [.] rsaz_1024_mul_avx2

  更新的硬件應該使用通用的montgomery模乘算法和ADX codepath。

  7.08% nginx [.] sqrx8x_internal

  2.30% nginx [.] mulx4x_internal

  對稱加密

  如果有許多的批量傳輸,如視頻、照片或更通用的文件,需要在分析器的輸出中觀察對稱加密符號。只需要確保CPU具有AES-NI支持,并為AES-GCM ciphers設置服務器端的首選項。根據perf top信息適當的調整硬件。

  8.47% nginx [.] aesni_ctr32_ghash_6x

  不只是服務器需要解決加密/解密問題,客戶端在缺少可用的CPU時也會面臨相同的負擔。如果沒有硬件加速,這可能非常具有挑戰性,因此可以考慮使用一種致力于快速而沒有硬件加速的算法,例如chacha20-poly1305。這將減少一些移動客戶的TTLB。

  BoringSSL是支持chacha20-poly1305的,而對于OpenSSL 1.0.2,可以考慮使用Cloudflare補丁。BoringSSL還支持“相等的首選密碼組”,因此可以使用以下配置,讓客戶端根據其硬件功能來決定使用什么密碼(從cloudflare/sslconfig中竊取)。

  ssl_ciphers '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]:ECDHE+AES128:RSA+AES128:ECDHE+AES256:RSA+AES256:ECDHE+3DES:RSA+3DES';

  ssl_prefer_server_ciphers on;

  應用程序級別:高標準的

  為了分析高標準的優化效果,需要收集RUM數據。在瀏覽器中,可以使用導航定時api和資源定時api收集。收集的主要指標是TTFB和TTV/TTI。擁有便于查詢和圖形化格式的數據將極大地簡化迭代。

  壓縮

  nginx的壓縮從mime.types文件開始,它定義了文件擴展和響應MIME類型之間的默認通信。然后需要定義傳遞給壓縮器的類型。如果想要完整的列表,可以使用mime-db來自動生成mime.types并添加.compressible==true到gzip_types。

  在啟用gzip時,兩個方面要注意。

  增加了內存的使用,可以通過限制gzip_buffer來解決。

  因為緩沖,所以增加了TTFB。使用[gzip_no_buffer](http://hg.nginx.org/nginx/file/c7d4017c8876/src/http/modules/ngx_http_gzip_filter_module.c # l182)可以解決問題。

  附注,http壓縮不僅限于gzip,nginx有第三方ngx_brotli模塊,與gzip相比改善后的壓縮率高達30%。

  至于壓縮設置本身,討論兩個單獨的用例:靜態和動態數據。

  對于靜態數據,可以通過預壓縮靜態資產作為構建過程的一部分來歸檔最大壓縮比。過去有討論在部署Brotli中為gzip和Brotli提供靜態內容的詳細信息。

  對于動態數據,需要謹慎地平衡一次完整的往返:壓縮數據的時間和傳輸時間,以便在客戶機上解壓。因此從CPU的使用和TTFB的角度來看,設置盡可能高的壓縮級別是不明智的。

  在代理中緩存會極大地影響web服務器性能,特別是在延遲方面。nginx代理模塊有不同的緩沖開關,它們在每個位置上都是可轉換的。可以通過proxy_request_buffering和proxy_buffering對兩個方向的緩沖進行單獨控制。如果緩沖是啟用了內存消耗上限的話,則由client_body_buffer_size和proxy_buffer設置,達到請求/響應的臨界值后,將緩沖到磁盤。對于響應的臨界值,可以通過將proxy_max_temp_file_size設置為0來禁用。

  最常見的緩沖方法有:

  緩沖請求/響應在內存中的某個閾值,然后溢出到磁盤。如果啟用了請求緩沖,那么只需將請求發送到后端,一旦它被完全接收,并通過響應緩沖,就可以在響應完成后立即釋放后端線程。這種方法的好處是提高了吞吐量和后端保護,以增加延遲和內存/io的使用(如果使用ssd,這可能不是什么問題)。

  沒有緩沖。緩沖可能不是延遲敏感路由的好選擇,特別是那些使用數據流的。對于他們來說可能想要禁用它,但是現在的后端需要處理慢速客戶端(包括惡意的慢提交/慢讀類型的攻擊)。

  Application-controlled響應緩沖通過[X-Accel-Buffering](https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/ # X-Accel-Buffering)數據頭。

  無論你選擇哪種方法,都不要忘記測試它對TTFB和TTLB的影響。另外,正如前面提到的,緩沖可以影響IO的使用,甚至是后端使用率,因此也要密切關注它。

  TLS

  現在我們將討論TLS的高級方面和延遲改進,可以通過正確配置nginx來實現。文章提到的大多數優化都包含在高性能瀏覽器網絡的“優化TLS”部分,并在nginx.conf 2014上讓HTTPS更快的討論。此部分中提到的調優將會影響web服務器的性能和安全性,如果不確定,請參考Mozilla服務器端TLS指南和/或與安全團隊協商。

  如何驗證優化結果。

  WebpageTest測試性能影響。

  采用來自Qualys的SSL服務器測試,或Mozilla TLS Observatory測試安全影響。

  會話重用

  像DBA常說的那樣,“最快的查詢是從來沒有做過的。”TLS同樣如此,如果你緩存了握手的結果,可以減少一個RTT的延遲。有兩種方法緩存握手結果。

  要求客戶端存儲所有會話參數(以簽名和加密的方式),并在下一次握手時返回(類似于cookie)。在nginx方面,這是通過ssl_session_tickets指令配置的。雖然不會消耗服務器的任何內存,但有一些缺點:

  需要基礎設施來創建、旋轉和分發LS會話的隨機加密/簽名鍵。請記住,不應該使用源控件來存儲ticket keys,也不應該從其他非臨時的材料中生成這些鍵,比如日期或證書。

  PFS不會在每個會話的基礎上,而是在每個tls-ticket的基礎上,因此如果攻擊者獲得了ticket key,他們就可以在ticket的持續時間內對任何捕獲的流量進行解密。

  加密將限制票務鑰匙的大小。如果使用的是128位的票證,那么使用AES256沒有多大意義,因為Nginx同時支持128位和256位TLS票號。

  并不是所有的客戶都支持ticket key(盡管所有的現代瀏覽器都支持它們)。

  或者,可以在服務器上存儲TLS會話參數,只給客戶端提供一個引用(id),通過ssl_session_cache指令完成。它的好處是在會話之間保留PFS,并極大地限制表面的攻擊。當然ticket keys也有缺點:

  在服務器上每次會話消耗約256字節的內存,這意味著不能將它們存儲太長時間。

  不能在服務器之間輕松共享。因此要么需要負載平衡器(loadbalancer ,發送相同的客戶端到相同的服務器以保存緩存位置),要么寫一個分布式TLS會話存儲類似ngx_http_lua_module。

  附注一點,如果使用session ticket方法,那么值得使用3個鍵而不是一個鍵值,例如:

  ssl_session_tickets on;

  ssl_session_timeout 1h;

  ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_curr;

  ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_prev;

  ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_next;

  即便始終使用當前的密鑰進行會話加密,也會接受使用之前和之后密鑰加密的會話。

  OCSP Stapling

  需要對OCSP響應作staple,否則:

  TLS握手可能需要更長的時間,因為客戶端需要與證書頒發機構聯系以獲取OCSP狀態。

  在OCSP的取回失敗可能導致可用性攻擊。

  可能會破壞用戶的隱私,因為用戶的瀏覽器會聯系第三方服務,表明想要連接到當前網站。

  要staple OCSP響應,可以定期從證書頒發機構獲取它,將結果分發給web服務器,并使用ssl_stapling_file指令來調用。

  ssl_stapling_file /var/cache/nginx/ocsp/www.der;

  TLS記錄大小

  TLS將數據分解成記錄塊,在完全接收到它之前,無法對其進行驗證和解密。可以從網絡堆棧和應用程序的角度來度量這一延遲。

  默認的nginx使用16k塊,甚至不適合IW10擁塞窗口,因此需要額外的往返。nginx提供了一種通過ssl_buffer_size指令設置記錄大小的方法:

  為了優化低延遲,你應該把它設置成小的,例如4k。從CPU使用的角度來看,進一步減少它將會更加昂貴。

  為了優化高通量,您應該將其保留在16k。

  靜態調優的兩個問題。

  需要手動調優它。

  只能將ssl_buffer_size設置為per-nginx config或per-server塊,因此如果有一個具有混合延遲/吞吐量工作負載的服務器,則需要折衷。

  還有一種替代方法:動態記錄大小調整。來自Cloudflare的nginx補丁為動態記錄大小提供了支持。最初對它進行配置可能是一種痛苦,但一旦配置完成,它就會運行得很好。

  TLS 1.3

  TLS 1.3的功能確實很不錯,但是除非有足夠的資源來解決TLS的問題,否則不建議啟用它,因為以下原因。

  它仍然是一個草案。

  0-RTT握手有一些安全隱患,應用程序需要為此做好準備。

  仍然有一些中間盒子(反病毒,DPIs等)阻止未知的TLS版本。

  避免Eventloop Stalls

  Nginx是一個基于事件循環的web服務器,意味著它只能一次只做一件事。盡管它似乎同時做了所有這些事情,比如在分時復用中,所有的nginx都只是在事件之間快速切換,處理一個接一個。這一切都有效,因為處理每個事件只需幾微秒。但如果它開始花費太多時間,例如,因為它需要轉到旋轉盤上,延遲就會飆升。

  如果注意到nginx在ngx_process_events_and_timer函數中花費了很多時間,并且分布是雙向的,那么可能會受到eventloop stalls的影響。

  # funclatency '/srv/nginx-bazel/sbin/nginx:ngx_process_events_and_timers' -m

  msecs : count distribution

  0 -> 1 : 3799 |****************************************|

  2 -> 3 : 0 | |

  4 -> 7 : 0 | |

  8 -> 15 : 0 | |

  16 -> 31 : 409 |**** |

  32 -> 63 : 313 |*** |

  64 -> 127 : 128 |* |

  AIO和Threadpools

  因為AIO和Threadpools是eventloop的主要來源,特別是在旋轉磁盤上的IO,所以應該優先查看,還可以通過運行文件記錄來觀察受它影響的程度。

  # fileslower 10

  Tracing sync read/writes slower than 10 ms

  TIME(s) COMM TID D BYTES LAT(ms) FILENAME

  2.642 nginx 69097 R 5242880 12.18 0002121812

  4.760 nginx 69754 W 8192 42.08 0002121598

  4.760 nginx 69435 W 2852 42.39 0002121845

  4.760 nginx 69088 W 2852 41.83 0002121854

  為了解決這個問題,nginx支持將IO卸載到一個threadpool(它也支持AIO,但是Unixes的本地AIO很不友好,所以最好避免它),基本的設置很簡單。

  aio threads;

  aio_write on;

  對于更復雜的情況可以設置自定義[線程池](http://nginx.org/en/docs/ngx_core_module.html # thread_pool)的預留磁盤,如果一個驅動器失效,它不會影響其他的請求。線程池可以極大地減少處于D狀態的nginx進程的數量,從而提高延遲和吞吐量。但是它不會完全消除eventloop,因為并不是所有IO操作都被卸載。

  日志

  記錄日志也會花費相當多的時間,因為它在讀寫磁盤。可以通過運行ext4slower檢查是否存在日志,并查找access/error日志引用。

  # ext4slower 10

  TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME

  06:26:03 nginx 69094 W 163070 634126 18.78 access.log

  06:26:08 nginx 69094 W 151 126029 37.35 error.log

  06:26:13 nginx 69082 W 153168 638728 159.96 access.log

  通過使用access_log指令的緩沖區參數,在編寫它們之前,通過在內存中對訪問日志進行欺騙,可以解決這個問題。再使用gzip參數,將日志寫入磁盤之前壓縮日志,從而減少IO壓力。

  但是要在日志中完全消除IO檔位,只能通過syslog編寫日志,這樣日志將與nginx事件循環完全集成。

  打開文件緩存

  因為open(2)調用本質上是阻塞的,而web服務器通常是打開/讀取/關閉文件,因此緩存打開的文件可能是有益的。通過查看ngx_open_cached_file函數延遲,可以看到便利所在。

  # funclatency /srv/nginx-bazel/sbin/nginx:ngx_open_cached_file -u

  usecs : count distribution

  0 -> 1 : 10219 |****************************************|

  2 -> 3 : 21 | |

  4 -> 7 : 3 | |

  8 -> 15 : 1 | |

  如果看到有太多的開放調用,或者有一些花費太多時間的調用,可以啟用文件緩存。

  open_file_cache max=10000;

  open_file_cache_min_uses 2;

  open_file_cache_errors on;

  在啟用open_file_cache之后,可以通過查看opensnoop來觀察所有的緩存遺漏,最終決定是否需要調整緩存限制。

  # opensnoop -n nginx

  PID COMM FD ERR PATH

  69435 nginx 311 0 /srv/site/assets/serviceworker.js

  69086 nginx 158 0 /srv/site/error/404.html

  ...

  總結

  本文描述的所有優化都是基于本地的Web服務器。其中一些提高了拓展性和性能。另外一些與最小延遲或更快地將字節傳送給客戶機是相關的。但在以往的體驗中,大量用戶可見的性能來自于更高級的優化,這些優化會影響到整個Dropbox 邊緣網絡的行為,比如ingress/egress流量工程和智能的內部負載平衡。這些問題處于知識的邊緣,而行業才剛剛開始接近它們。

  -------- 熱聞回顧 --------

  2017年最受歡迎的10個編程挑戰網站

  電話咨詢

<abbr id="ehohq"><form id="ehohq"><small id="ehohq"></small></form></abbr>
  • <menu id="ehohq"><dl id="ehohq"></dl></menu>

    1. <bdo id="ehohq"><span id="ehohq"></span></bdo>
        主站蜘蛛池模板: 民乐县| 颍上县| 荥经县| 凯里市| 万安县| 分宜县| 孙吴县| 大宁县| 普定县| 勐海县| 和林格尔县| 库尔勒市| 驻马店市| 子长县| 延长县| 南溪县| 炉霍县| 筠连县| 东平县| 沅江市| 荥阳市| 嘉善县| 通城县| 灯塔市| 东丽区| 都匀市| 利川市| 咸丰县| 瑞丽市| 临夏市| 彭泽县| 光山县| 育儿| 西丰县| 来安县| 秭归县| 和硕县| 南木林县| 嵩明县| 金华市| 大方县|