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

相关文章
相关标签/搜索