C10K 问题
随着互联网的蓬勃发展,一个非常重要的问题摆在计算机工业界面前。这个问题就是如何使用最低的成本满足高性能和高并发的需求。这个问题在过去可能不是一个严重的问题,但是在 2000 年前后,互联网用户的人数井喷,如果说之前单机服务的用户数量还保持在一个比较低的水平,比如说只有上百个用户,那么在互联网逐渐普及的情况下,服务于成千上万个用户就将是非常普遍的情形,在这种情形下,如果还按照之前单机的玩法,成本就将超过人们想象,只有超级有钱的大玩家才可以继续下去。
于是,C10K 问题应运而生。C10K 问题是这样的:如何在一台物理机上同时服务 10000 个用户?这里 C 表示并发,10K 等于 10000。得益于操作系统、编程语言的发展,在现在的条件下,普通用户使用 Java Netty、Libevent 等框架或库就可以轻轻松松写出支持并发超过 10000 的服务器端程序,甚至于经过优化之后可以达到十万,乃至百万的并发,但在二十年前,突破 C10K 问题可费了不少的心思,是一个了不起的突破。
操作系统层面
C10K 问题本质上是一个操作系统问题,要在一台主机上同时支持 1 万个连接需要考虑以下方面:
文件句柄
首先我们知道每个客户连接都代表一个文件描述符,一旦文件描述符不够用了,新的连接就会被放弃,产生如下的错误:
Socket/File:Can't open so many files
在 Linux 下,单个进程打开的文件句柄数是有限制的,没有经过修改的值一般都是 1024。
$ulimit -n
1024
这意味着最多可以服务的连接数上限只能是 1024。不过,我们可以对这个值进行修改,比如用 root 权限修改 /etc/sysctl.conf 文件,使得系统可用支持 10000 个描述符上限。
fs.file-max = 10000
net.ipv4.ip_conntrack_max = 10000
net.ipv4.netfilter.ip_conntrack_max = 10000
系统内存
每个 TCP 连接占用的资源可不止一个连接套接字这么简单,在前面的章节中,我们多少接触到了类似发送缓冲区、接收缓冲区这些概念。每个 TCP 连接都需要占用一定的发送缓冲区和接收缓冲区。
下面一段 shell 代码,分别显示了在 Linux 4.4.0 下发送缓冲区和接收缓冲区的值。
$cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 4194304
$ cat /proc/sys/net/ipv4/tcp_rmem
4096 87380 6291456
</