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


現在的服務器支撐上百萬個並發 TCP 連接已經不是新聞(余鋒2010年的演講ideawuiComet 開源項目WhatsApp 做到了 2.5M)。實現 C1000k 的常規做法是調整內核參數,提高文件數,降低每個連接的內存消耗(參考 ideawu 的博客)。

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

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

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

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

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:2009


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM