讀 www.52im.net/thread-561-… 筆記。html
一個tcp鏈接都要佔一個文件描述符,一旦這個文件描述符使用完了,新的鏈接到來返回給咱們的錯誤是「Socket/File:Can't open so many files」
。linux
進程限制shell
執行 ulimit -n 輸出 1024,說明對於一個進程而言最多隻能打開1024個文件,因此你要採用此默認配置最多也就能夠併發上千個TCP鏈接。臨時修改:ulimit -n 1000000,可是這種臨時修改只對當前登陸用戶目前的使用環境有效,系統重啓或用戶退出後就會失效。編程
重啓後失效的修改(不過我在CentOS 6.5下測試,重啓後未發現失效),編輯 /etc/security/limits.conf 文件, 修改後內容爲:數組
soft nofile 1000000
hard nofile 1000000
複製代碼
永久修改:編輯/etc/rc.local,在其後添加以下內容:緩存
ulimit -SHn 1000000
複製代碼
全侷限制服務器
執行 cat /proc/sys/fs/file-nr
輸出 9344 0 592026,分別爲:網絡
但在kernel 2.6版本中第二項的值總爲0,這並非一個錯誤,它實際上意味着已經分配的文件描述符無一浪費的都已經被使用了 。多線程
咱們能夠把這個數值改大些,用 root 權限修改 /etc/sysctl.conf
文件:架構
fs.file-max = 1000000
net.ipv4.ip_conntrack_max = 1000000
net.ipv4.netfilter.ip_conntrack_max = 1000000
複製代碼
最初的服務器都是基於進程/線程模型的,新到來一個TCP鏈接,就須要分配1個進程(或者線程)。而進程又是操做系統最昂貴的資源,一臺機器沒法建立不少進程。若是是C10K就要建立1萬個進程,那麼單機而言操做系統是沒法承受的(每每出現效率低下甚至徹底癱瘓)。
建立的進程線程多了,數據拷貝頻繁(緩存I/O、內核將數據拷貝到用戶進程空間、阻塞), 進程/線程上下文切換消耗大, 致使操做系統崩潰,這就是C10K問題的本質!
可見,解決C10K問題的關鍵就是儘量減小這些CPU等核心計算資源消耗,從而榨乾單臺服務器的性能,突破C10K問題所描述的瓶頸。
思路:每一個進程/線程同時處理多個鏈接(IO多路複用)
該思路的實現存在如下歷程:
截至目前,40gpbs、32-cores、256G RAM
的X86服務器在Newegg網站上的報價是幾千美圓。實際上以這樣的硬件配置來看,它徹底能夠處理1000萬個以上的併發鏈接,若是它們不能,那是由於你選擇了錯誤的軟件,而不是底層硬件的問題。
能夠預見在接下來的10年裏,由於IPv6協議下每一個服務器的潛在鏈接數都是數以百萬級的,單機服務器處理數百萬的併發鏈接(甚至千萬)並不是不可能,但咱們須要從新審視目前主流OS針對網絡編程這一塊的具體技術實現。
Unix的設計初衷並非通常的服務器操做系統,而是電話網絡的控制系統。因爲是實際傳送數據的電話網絡,因此在控制層和數據層之間有明確的界限。問題是咱們如今根本不該該使用Unix服務器做爲數據層的一部分。
不要讓OS內核執行全部繁重的任務:將數據包處理、內存管理、處理器調度等任務從內核轉移到應用程序高效地完成,讓諸如Linux這樣的OS只處理控制層,數據層徹底交給應用程序來處理。
綜上所述,解決C10M問題的關鍵主要是從下面幾個方面入手:
**網卡問題:**經過內核工做效率不高 **解決方案:**使用本身的驅動程序並管理它們,使適配器遠離操做系統。
**CPU問題:**使用傳統的內核方法來協調你的應用程序是行不通的。 **解決方案:**Linux管理前兩個CPU,你的應用程序管理其他的CPU,中斷只發生在你容許的CPU上。
**內存問題:**內存須要特別關注,以求高效。 **解決方案:**在系統啓動時就分配大部份內存給你管理的大內存頁。
以Linux爲例,解決的思路就是將控制層交給Linux,應用程序管理數據。應用程序與內核之間沒有交互、沒有線程調度、沒有系統調用、沒有中斷,什麼都沒有。
不管是多進程模型仍是多線程模型,都要把全部的調度任務交給操做系統,讓操做系統幫咱們分配硬件資源。咱們經常使用的服務器操做系統都屬於分時操做系統,調度模型都儘量的追求公平,並無爲某一類任務作特別的優化,若是當前系統僅僅運行某一特定任務的時候,默認的調度策略可能會致使必定程度上的性能損失。我運行一個A任務,第一個調度週期在0號核心上運行,第二個調度週期可能就跑到1號核心上去了,這樣頻繁的調度可能會形成大量的上下文切換,從而影響到必定的性能。
數據局域性是一樣相似的問題。當前x86服務器以NUMA架構爲主,這種平臺架構下,每一個CPU有屬於本身的內存,若是當前CPU須要的數據須要到另一顆CPU管理的內存獲取,必然增長一些延時。因此咱們儘量的嘗試讓咱們的任務和數據在始終在相同的CPU核心和相同的內存節點上,Linux提供了sched_set_affinity
函數,咱們能夠在代碼中,將咱們的任務綁定在指定的CPU核心上。一些Linux發行版也在用戶態中提供了numactl
和taskset
工具,經過它們也很容易讓咱們的程序運行在指定的節點上。
這些技術都是近些年來爲了優化Linux網絡方面的性能而添加的特性,RPS、RFS、XPS都是Google貢獻給社區,RSS須要硬件的支持,目前主流的網卡都已支持,即俗稱的多隊列網卡,充分利用多個CPU核心,讓數據處理的壓力分佈到多個CPU核心上去。
RPS和RFS在linux2.6.35的版本被加入,通常是成對使用的,在不支持RSS特性的網卡上,用軟件來模擬相似的功能,而且將相同的數據流綁定到指定的核心上,儘量提高網絡方面處理的性能。XPS特性在linux2.6.38的版本中被加入,主要針對多隊列網卡在發送數據時的優化,當你發送數據包時,能夠根據CPU MAP來選擇對應的網卡隊列,低於指定的kernel版本可能沒法使用相關的特性,可是發行版已經backport這些特性。
關於IRQ的優化,這裏主要有兩點,第一點是關於中斷合併。在比較早期的時候,網卡每收到一個數據包就會觸發一箇中斷,若是小包的數據量特別大的時候,中斷被觸發的數量也變的十分可怕。大部分的計算資源都被用於處理中斷,致使性能降低。後來引入了NAPI和Newernewer NAPI特性,在系統較爲繁忙的時候,一次中斷觸發後,接下來用輪循的方式讀取後續的數據包,以下降中斷產生的數量,進而也提高了處理的效率。第二點是IRQ親和性,和咱們前面提到了CPU親和性較爲相似,是將不一樣的網卡隊列中斷處理綁定到指定的CPU核心上去,適用於擁有RSS特性的網卡。
這裏再說說關於網絡卸載的優化,目前主要有TSO、GSO、LRO、GRO這幾個特性,先說說TSO,以太網MTU通常爲1500,減掉TCP/IP的包頭,TCP的MaxSegment Size爲1460,一般狀況下協議棧會對超過1460的TCP Payload進行分段,保證最後生成的IP包不超過MTU的大小,對於支持TSO/GSO的網卡來講,協議棧就再也不須要這樣了,能夠將更大的TCPPayload發送給網卡驅動,而後由網卡進行封包操做。經過這個手段,將須要在CPU上的計算offload到網卡上,進一步提高總體的性能。GSO爲TSO的升級版,不在侷限於TCP協議。LRO和TSO的工做路徑正好相反,在頻繁收到小包時,每次一個小包都要向協議棧傳遞,對多個TCPPayload包進行合併,而後再傳遞給協議棧,以此來提高協議棧處理的效率。GRO爲LRO的升級版本,解決了LRO存在的一些問題。這些特性都是在必定的場景下才能夠發揮其性能效率,在不明確本身的需求的時候,開啓這些特性反而可能形成性能降低。
關於Kernel的網絡相關優化咱們就不過多的介紹了,主要的內核網絡參數的調整在如下兩處:net.ipv4.*參數和net.core.*參數。
主要用於調節一些超時控制及緩存等,經過搜索引擎咱們能很容易找到關於這些參數調優的文章,可是修改這些參數是否能帶來性能的提高,或者會有什麼弊端,建議詳細的閱讀kernel文檔,而且多作一些測試來驗證。