持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
最近在尝试着解决,c10k 问题,在之前的文章解决C10K 问题操作系统 内容配置 详解,那我们修改系统配置之后,应该做些什么呢? 修改过这些配置之后,只能代表操作系统目前的设置不会成为我们建立大量的连接。但是这么多连接我们应该怎么处理呢?
用线程或者协程,每个进程/线程 来处理一个连接
我们很直观的可以想到用多线程来处理这个问题,我们可以为每一个连接分配一个线程或者协程来管理这个连接,尽管是占用资源很少的协程来说,面对百万的连接来说占用的资源也是相当可观的,同时可能对系统的线程和进程造成压力,因此这种方法弊端有些大。
### 每个进程/线程同时处理多个连接(IO多路复用)
I/O 多路复用也可以分为很多种。
阻塞式 I/O
传统思路最简单的方法是循环挨个处理各个连接,每个连接对应一个 socket,当所有 socket 都有数据的时候,这种方法是可行的。但是当应用读取某个 socket 的文件数据不 ready 的时候,整个应用会阻塞在这里等待该文件句柄,即使别的文件句柄 ready,也无法往下处理。
缺点:任一文件句柄的不成功会阻塞住整个应用。
SELECT
select要解决上面阻塞的问题,思路很简单,如果我在读取文件句柄之前,先查下它的状态,ready 了就进行处理,不 ready 就不进行处理,这不就解决了这个问题了嘛?于是有了 select 方案。用一个 fd_set 结构体来告诉内核同时监控多个文件句柄,当其中有文件句柄的状态发生指定变化(例如某句柄由不可用变为可用)或超时,则调用返回。之后应用可以使用 FD_ISSET 来逐个查看是哪个文件句柄的状态发生了变化。这样做,小规模的连接问题不大,但当连接数很多(文件句柄个数很多)的时候,逐个检查状态就很慢了。因此,select 往往存在管理的句柄上限(FD_SETSIZE)。同时,在使用上,因为只有一个字段记录关注和发生事件,每次调用之前要重新初始化 fd_set 结构体。
句柄上限+重复初始化+逐个排查所有文件句柄状态效率不高。
Poll
poll 主要解决 select 的前两个问题:通过一个 pollfd 数组向内核传递需要关注的事件消除文件句柄上限,同时使用不同字段分别标注关注事件和发生事件,来避免重复初始化。
逐个排查所有文件句柄状态效率不高。
epoll
epoll既然逐个排查所有文件句柄状态效率不高,很自然的,如果调用返回的时候只给应用提供发生了状态变化(很可能是数据 ready)的文件句柄,进行排查的效率不就高多了么。epoll 采用了这种设计,适用于大规模的应用场景。实验表明,当文件句柄数目超过 10 之后,epoll 性能将优于 select 和 poll;当文件句柄数目达到 10K 的时候,epoll 已经超过 select 和 poll 两个数量级。
目前效果最好