首页 > 编程知识 正文

linux性能调优指南,linux性能调优书籍

时间:2023-05-04 11:53:24 阅读:265848 作者:3413

C10K
1999年由Dan Kegel ,那时的服务器运行着LINUX2.2系统,32位系统,内存很少(2G),千兆网卡。
怎么在这样的系统中支持并发 1 万的请求呢?
先来计算一下:
最大每个连接的内存:2GB / 10000 = 200kb
最大每个连接的带宽: 1000Mbit/10000 = 100bit
所以只要每个连接的内存不大于200KB,带宽不大于100bit,物理资源三足够的。
剩下的就是软件问题:
在 C10K 以前,Linux 中网络处理都用同步阻塞的方式,也就是每个请求都分配一个进程或者线程。
线程数低于100,那肯定是没有问题的,但是连接数目到达10000个的话,10000 个进程或线程的调度、上下文切换乃至它们占用的内存,都会成为瓶颈。
这就有两个问题要解决:
1 一个线程怎么处理多个IO,是否可以用异步或者非阻塞
2 更节省资源的方式来服务客户
I/O 模型优化
两种 I/O 事件通知的方式:
水平触发:只要文件描述符可以非阻塞地执行 I/O ,就会触发通知。也就是说,应用程序可以随时检查文件描述符的状态,然后再根据状态,进行 I/O 操作。
边缘触发:只有在文件描述符的状态发生改变(也就是 I/O 请求达到)时,才发送一次通知。这时候,应用程序需要尽可能多地执行 I/O,直到无法继续读写,才可以停止。如果 I/O 没执行完,或者因为某种原因没来得及处理,那么这次通知也就丢失了。
I/O 多路复用的实现方法:
1 使用非阻塞 I/O 和水平触发通知,比如使用 select 或者 poll。
优点:是对应用程序比较友好,它的 API 非常简单
缺点:效率比较低,select有文件描述符的限制,需要两层循环,时间复杂度变为o(n^2),polll处理耗时跟文件描述符是o(n)。文件描述符需要从用户空间传到内核空间,内核修改后,在返回用户空间。
2 使用非阻塞 I/O 和边缘触发通知,比如 epoll。
使用红黑树和事件驱动的机制,提高效率。但是只有IO发生的时候才会通知。需要应用程序做更多的IO操作和处理异常事件。
3 使用异步 I/O(Asynchronous I/O,简称为 AIO)
工作模型优化
1 主进程 + 多个 worker 子进程,这也是最常用的一种模型
1 主进程执行 bind() + listen() 后,创建多个子进程;
2 然后,在每个子进程中,都通过 accept() 或 epoll_wait() ,来处理相同的套接字。
会出现epoll惊群问题:当网络 I/O 事件发生时,多个进程被同时唤醒,但实际上只有一个进程来响应这个事件,其他被唤醒的进程都会重新休眠。
Nginx 的做法是在每个 worker 进程中,都增加一个了全局锁(accept_mutex)。这些 worker 进程需要首先竞争到锁,只有竞争到锁的进程,才会加入到 epoll 中,这样就确保只有一个 worker 子进程被唤醒。
也可以用线程代替进程:主线程负责套接字初始化和子线程状态的管理,而子线程则负责实际的请求处理。由于线程的调度和切换成本比较低,实际上你可以进一步把 epoll_wait() 都放到主线程中,保证每次事件都只唤醒主线程,而子线程只需要负责后续的请求处理。
2 监听到相同端口的多进程模型。
每个进程都监听到同一个端口,由内核做负载均衡,唤醒进程,避免了惊群问题。需要在linux 3.9 以上打开SO_REUSEPORT
C1000K
从 1 万到 10 万,其实还是基于 C10K 的这些理论,epoll 配合线程池,再加上 CPU、内存和网络接口的性能和容量提升。大部分情况下,C100K 很自然就可以达到。
但是到100万就没有那么简单了
物理资源上:
1 假设每个请求需要 16KB 内存的话,那么总共就需要大约 15 GB 内存。
2 从带宽上来说,假设只有 20% 活跃连接,即使每个连接只需要 1KB/s 的吞吐量,总共也需要 1.6 Gb/s 的吞吐量。千兆网卡显然满足不了这么大的吞吐量,所以还需要配置万兆网卡,或者基于多网卡 Bonding 承载更大的吞吐量。
软件资源上:
大量的连接也会占用大量的软件资源,比如文件描述符的数量、连接状态的跟踪(CONNTRACK)、网络协议栈的缓存大小(比如套接字读写缓存、TCP 读写缓存)等等。
C1000K 的解决方法,本质上还是构建在 epoll 的非阻塞 I/O 模型上。只不过,除了 I/O 模型之外,还需要从应用程序到 Linux 内核、再到 CPU、内存和网络等各个层次的深度优化,特别是需要借助硬件,来卸载那些原来通过软件处理的大量功能。
C10M
在 C1000K 问题中,各种软件、硬件的优化很可能都已经做到头了。无乱怎么调试参数,提升硬件都到头了。根本的问题是,LINUX网络协议栈做了太多太繁重的工作。
解决这个问题,那就是跳过linux网路协议栈,常见的解决方案是DPDK 和 XDP。
第一种机制,DPDK,是用户态网络的标准。它跳过内核协议栈,直接由用户态进程通过轮询的方式,来处理网络接收。

在 PPS 非常高的场景中,查询时间比实际工作时间少了很多,绝大部分时间都在处理网络包;而跳过内核协议栈后,就省去了繁杂的硬中断、软中断再到 Linux 网络协议栈逐层处理的过程,应用程序可以针对应用的实际场景,有针对性地优化网络包的处理逻辑,而不需要关注所有的细节。
DPDK 还通过大页、CPU 绑定、内存对齐、流水线并发等多种机制,优化网络包的处理效率。
第二种机制,XDP(eXpress Data Path),则是 Linux 内核提供的一种高性能网络数据路径。它允许网络包,在进入内核协议栈之前,就进行处理,也可以带来更高的性能。XDP 底层跟我们之前用到的 bcc-tools 一样,都是基于 Linux 内核的 eBPF 机制实现的。

XDP 的应用程序通常是专用的网络应用,常见的有 IDS(入侵检测系统)、DDoS 防御、 cilium 容器网络插件等。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。