UNP-性能-C10K

C10K问题

如何在一台物理机上同时服务 10000 个用户?这里 C 表示并发,10K 等于 10000。

现在的条件下,普通用户使用 Java Netty、Libevent 等框架或库就可以轻轻松松写出支持并发超过 10000 的服务器端程序,甚至于经过优化之后可以达到十万,乃至百万的并发,但在二十年前,突破 C10K 问题可费了不少的心思,是一个了不起的突破。

操作系统层

文件句柄

在linux,单个进程打开的文件句柄是有限制的,一般默认为1024个。这意味着,最多可以服务的上限只能是1024个。不过,我们可以修改它

fs.file-max = 10000
net.ipv4.ip_conntrack_max = 10000
net.ipv4.netfilter.ip_conntrack_max = 10000

系统内存

每个TCP连接占用的资源可不止一个套接字那么简单,类似发送缓冲区,接受缓冲区,都是了不起的内存消耗。下面的代码展示了linux4.4.0下发送缓冲区,接受缓冲区的值。

// 分别打印出最小分配,默认分配,最大分配。
$cat   /proc/sys/net/ipv4/tcp_wmem
4096  16384  4194304
$ cat   /proc/sys/net/ipv4/tcp_rmem
4096  87380  6291456

以默认分配为例:一万个连接的内存消耗为:1638410000+87380100000 = 160M+880M。大概一个多G。其实这是可以接受的哈,由此可见,内存并不是大瓶颈

网络带宽

假设10000个连接,每个连接每秒传输1KB数据, 需要100001KB/s8 = 80Mbps。如今的网络应付起来小菜一碟

总结来说,操作系统层面不是C10k问题的大障碍
BUT!这只是理论上不拉我们后腿而已,设计不好的程序可能在低并发下工作良好,一旦遇到高并发情形,其性能会指数下降。

我们需要从两个层面考虑设计C10k程序,考虑这几个层面,我们会得到几种常见的解决方案

  1. 应用程序如何和操作系统配合,感知I/O事件,并调度处理上万个套接字? ----> 阻塞/非阻塞I/O
  2. 应用程序如何分配进程,线程资源

解决方案

阻塞I/O+进程

简单,直接,粗暴。每个连接通过fork派生一个子进程进行处理,多个连接之间不受影响。
简单,效率差,扩展性差,资源占用高。

阻塞I/O+线程

线程相比进程更轻量一些,但创建进程还是比较耗资源,我们可以预先创建一个线程池。

非阻塞I/O+准备通知+单线程

这就是select/poll/epoll的I/O多路复用技术。

非阻塞I/O+准备通知+多线程

上面的做法是所有的I/O事件在一个线程里分发,我们可以引用现代CPU多核的能力,每个核心都能做一个I/O分发器
这就是大名鼎鼎的主从reactor模式。基于epoll/poll/select的I/O事件分发器可以叫reactor,也叫事件驱动,或者事件轮询(eventloop)。

异步I/O+多线程

这涉及Linux下aio机制。当调用结束后,立即返回,由操作系统后台完成对应的操作,当最终操作完成,就会产生一个信号,或者执行一个回调函数。

posted @ 2020-05-06 11:00  傻蜗牛  阅读(162)  评论(0)    收藏  举报