C1000k 新思路:用戶態 TCP/IP 協議棧

如今的服務器支撐上百萬個併發 TCP 鏈接已經不是新聞(餘鋒2010年的演講ideawuiComet 開源項目WhatsApp 作到了 2.5M)。實現 C1000k 的常規作法是調整內核參數,提升文件數,下降每一個鏈接的內存消耗(參考 ideawu 的博客)。html

在今年的 BSDCan2014 會議上, Patrick Kelsey 介紹了把 FreeBSD 9.x 的 TCP/IP 協議棧移植到了用戶態(slides, github.com/pkelsey/libuinet),並用於 WANProxy 項目。在用戶態運行 TCP/IP 協議棧意味着併發 TCP 鏈接再也不佔用系統文件數,只佔內存,解決了 C1000k 的一大瓶頸,內核只要提供一個收發網絡 packet 的接口就行(例如 netmap)。git

內核的網絡協議棧強調通用性,主要是爲吞吐量優化(性能指標一般是 MB/s 或 packets per second),順帶兼顧大量併發鏈接。爲了支持 C1000k,要調整內核參數讓每一個鏈接少佔資源,這與內核代碼的設計初衷是違背的。github

用戶態協議棧捅破了這層窗戶紙,能夠根據應用的特色來剪裁協議棧功能。優化也更直接,再也不是調黑盒參數組合,而是直接上 profiling,根據結果修改應用程序和協議棧的代碼。編程

用戶態協議棧的吞吐量比不上內核,不過對 C1000k 的應用場合(例如 comet)應該不成問題。vim

muduo 的 C1000k 實驗

我用 muduo 作了一次 C1000k 的實驗,用的是傳統方案,沒有用 libuinet。在一臺 16GB 內存的 Dell WS490 舊工做站上建立了 50萬個 TCP 鏈接,提供 echo 服務。系統可用內存減小了 5286MiB,即每一個鏈接 10.8KiB(其中服務進程佔用了 1421MiB 內存,即每一個鏈接 2.9KiB,其他 8KiB 左右是內核協議棧的開銷)。客戶端是一臺 8GB 內存的 i5-2500,內存消耗也是 5GB 多,所以此次實驗只試到了 C500k。客戶機綁定了 10 個 IP,每一個 IP 上發出 5 萬 TCP 鏈接,運行 pingpong 協議,每一個鏈接輪流收發 64 字節的消息。測得 QPS 大約是 11k,服務器的 CPU 佔用率約爲 60%(單線程)。profile 顯示 CPU 的主要開銷在內核中,我對這個結果基本滿意。數組

復活 4.4BSD-Lite2 的網絡協議棧

受 libuinet 啓發,我把 4.4BSD-Lite2 的網絡協議棧也移植到了 Linux 用戶態(github.com/chenshuo/4.4BSD-Lite2),方便《TCP/IP 詳解 第2卷》的讀者跟蹤調試其代碼。如下是 Eclipse CDT 單步跟蹤的截圖。服務器

eclipse

也能夠用各類現成的工具來分析函數的調用關係:網絡

bsd44

我在《談一談網絡編程學習經驗》中說這本書的「代碼只能看,不能上機運行,也不能改動試驗」現在再也不成立了。併發

我在《關於 TCP 併發鏈接的幾個思考題與試驗》中用 TAP/TUN 做爲本身寫的協議棧的對外接口,對 4.4BSD-Lite2 也可如法炮製,讓 20 年前的 TCP/IP 協議棧與如今的機器通訊。除了與本機通訊,還能夠經過 NAT 轉發,讓 4.4BSD-Lite2 連上如今的 Internet。(sudo iptables -t nat -A PREROUTING -p tcp --dport 2009 -i eth0 -j DNAT --to 192.168.0.2:2009eclipse

相關文章
相關標籤/搜索