如何解决C10K问题?
使用IO多路复用
select,使用一个fd_set来告诉内核要监控的文件句柄,然后当其中的socket状态发生变化或者超时这个调用就会返回,然后我们就可以在应用层使用轮训的方式来检查哪些socket有事件
缺点:句柄上限+轮训检查+重复初始化
poll使用,一个pollfd的一个结构体数组来向内核传递需要关注的事件,pollfd结构体有两个字段(events和revents)分别用来表示关注的事件和发生的事件,这个发生的事件的字段是可重用的,因此就避免了每次重复初始化,使用数组也解决了select有句柄上限的缺点,但是他没有解决select每次都要轮训检查哪些fd有事件的缺点
epoll是在epoll内核有个红黑树的结构来管理所有的文件描述符,当里面的socket有事件时会进入内核的就绪事件的链表中,然后我们调用epoll_wait就可以直接获取就绪事件的文件描述符,而不用我们每次在用户态轮训查看哪些socket有事件
水平触发也就是条件触发,也就是只要条件满足,就会触发一个事件(如果数据没取走的话内核就会不断通知)
边缘触发只有在socket状态改变的时候才会触发事件
java的nio是水平触发,这样就可以不用每次都把socket里面的数据读完,水平触发的话没读完内核也会继续通知
epoll如果使用边缘触发的话得配合非阻塞socket,因为边缘触发只触发一次事件,也就是他要把socket中的数据读空,如果使用阻塞socket就会在数据读完后一直阻塞,非阻塞socket读完就会返回
水平触发只要有数据可写,可读就会触发事件,在时效性上会好一点,但是开销更大,因为一份数据会触发多次事件,而边缘触发只有缓冲区发生改变,也就是从无到有,从有到无的时候会触发事件,所以每次都要把数据一次性处理完,以免遗漏数据
C1000K 是不是也可以很容易就实现呢?这其实没有那么简单了。首先从物理资源使用上来说,100 万个请求需要大量的系统资源。比如,其次,从软件资源上来说,大量的连接也会占用大量的软件资源,比如文件描述符的数量、连接状态的跟踪(CONNTRACK)、网络协议栈的缓存大小(比如套接字读写缓存、TCP读写缓存)等等。最后,大量请求带来的中断处理,也会带来非常高的处理成本。这样,就需要多队列网卡、中断负载均衡、CPU 绑定、RPS/RFS(软中断负载均衡到多个 CPU 核上),以及将网络包的处理卸载(Offload)到网络设备(如 TSO/GSO、LRO/GRO、VXLAN OFFLOAD)等各种硬件和软件的优化。C1000K 的解决方法,本质上还是构建在 epoll 的非阻塞 I/O 模型上。只不过,除了 I/O模型之外,还需要从应用程序到 Linux 内核、再到 CPU、内存和网络等各个层次的深度优化,特别是需要借助硬件,来卸载那些原来通过软件处理的大量功能。
C10M,轮训代替内核中断,实现用户态网络