这篇文章上次修改于 899 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
1 C10K
系统中支持 1 万并发请求。
- 一个线程中处理多个请求。可采用非阻塞或异步 I/O 方式。
- 用更少的线程服务请求。
1.1 I/O 模型优化
两种 I/O 事件通知方式:
- 水平触发,只要文件描述符可以非阻塞地触发通知,就可以触发 I/O。即应用程序可以随时地检查文件描述符状态,然后根据状态进行 I/O 操作。
- 边缘触发,只有在文件描述符状态发生改变时(即 I/O 请求到达),才发送一次通知。这时需要应用程序尽可能多地执行 I/O,直到无法读写。
I/O 多路复用方法:
第一种,采用非阻塞 I/O 和水平触发通知,如 select 和 poll。
- 优点:简单,对应用程序友好。
- 缺点:应用程序每次调用 select 或 poll 时,需要对文件描述符轮询,耗时较多。select 还有固定文件描述符数量限制。此外,每次调用 select 或 poll 时,还需要将文件描述符从用户空间传入内核,内核修改后再传回用户空间,存在切换开销。
第二种,使用非阻塞 I/O 和边缘触发通知,如 epoll。
- 使用红黑树在内核中管理文件描述符,不需要传入、传出文件描述符。
- 使用事件驱动机制,只需要关注有 I/O 事件发送的文件描述符,无需轮询。
- 第三种,使用异步 I/O。应用可同时发起多个 I/O 操作,而不需要等待 I/O 结束,I/O 完成后,系统会用事件通知的方式告知应用。
1.2 工作模型优化
第一种,主进程 + 多个子进程:
- 主进程执行 bind() + listen() 后,创建多个子进程。
- 在每个子进程中采用 accept() + epoll_wait() 处理相同的套接字。
对于惊群问题:
- accept() 惊群问题已在 Linux 2.6 中解决。
- epoll 惊群问题已在 Linux 4.5 中通过 EPOLLEXCLUSIVE 解决。
为避免进程频繁创建,这些进程在没任务时休眠,有任务时唤醒。
为降低切换开销,可用线程代替进程。甚至将 epoll_wait() 放在主线程中,确保每次事件只唤醒主线程。
第二种,监听到相同端口的多进程模型。所有的进程都监听相同端口,并开启 SO_REUSEPORT 选项,由内核将请求负载均衡到监听进程中。
2 C1000K
还需要从应用程序到 Linux 内核、再到 CPU、内存和网络等各个层次的深度优化,特别是需要借助硬件,来卸载那些原来通过软件处理的大量功能。
3 C10M
要解决这个问题,最重要就是跳过内核协议栈的冗长路径,把网络包直接送到要处理的应用程序那里去。这里有两种常见的机制,DPDK 和 XDP。
- XDP:在内核协议栈之前,先处理网络包。
- DPDK:跳过网络协议栈,在用户空间采用轮询方式。
参考
倪朋飞. Linux 性能优化实战.
没有评论