Nginx 高并发高性能实现
Nginx 高并发实现模型
Nginx 采用的是多进程(单线程)和多路IO复用模型。使用了 I/O 多路复用技术的 Nginx,就成了“并发事件驱动”的服务器。
Nginx 在启动后,会有一个 master 进程和多个相互独立的 worker 进程。master 接收来自外界的信号,向各 worker 进程发送信号,每个进程都有可能来处理这个连接。同时,master 进程能监控 worker 进程的运行状态,当 worker 进程退出后(异常情况下),会自动启动新的 worker 进程。
orker 进程数,一般会设置成机器 cpu 核数。因为更多的 worker 数,只会导致进程相互竞争 cpu,从而带来不必要的上下文切换。
使用多进程模式,不仅能提高并发率,而且进程之间相互独立,一个 worker 进程挂了不会影响到其他 worker 进程。
在 nginx 中的 work 进程中,为了应对高并发场景,采取了 Reactor 模型(也就是I/O多路复用,NIO):
- 每一个worker进程通过 I/O 多路复用处理多个连接请求;
- 为了减少进程切换(需要系统调用)的性能损耗,一般设置 worker 进程数量和CPU数量一致。
I/O 多路复用模型:在 I/O 多路复用模型中,最重要的系统调用函数就是 select(其他的还有epoll等),该方法的能够同时监控多个文件描述符的可读可写情况(每一个网络连接其实都对应一个文件描述符),当其中的某些文件描述符可读或者可写时,select 方法就会返回可读以及可写的文件描述符个数。
nginx work进程使用 I/O 多路复用模块(epoll)同时监听多个 FD(文件描述符),当 accept、read、write 和 close 事件产生时,操作系统就会回调 FD 绑定的事件处理器,这时候 work 进程再去处理相应事件,而不是阻塞在某个请求连接上等待。这样就可以实现一个进程同时处理多个连接。
Nginx 的异步非阻塞工作方式把当中的等待时间利用起来了。在需要等待的时候,这些进程就空闲出来待命了,因此表现为少数几个进程就解决了大量的并发问题。每进来一个 request,会有一个 worker 进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发 request,并等待请求返回。那么,这个处理的worker很聪明,他会在发送完请求后,注册一个事件:“如果 upstream 返回了,告诉我一声,我再接着干”。于是他就休息去了。此时,如果再有 request 进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker 才会来接手,这个 request 才会接着往下走。
为什么 Nginx 不使用多线程?
- Apache: 创建多个进程或线程,而每个进程或线程都会为其分配 cpu 和内存(线程要比进程小的多,所以 worker 支持比 perfork 高的并发),并发过大会耗光服务器资源。
- Nginx: 采用单线程来异步非阻塞处理请求(管理员可以配置 Nginx 主进程的工作进程的数量)(epoll),不会为每个请求分配 cpu 和内存资源,节省了大量资源,同时也减少了大量的 CPU 的上下文切换。所以才使得 Nginx 支持更高的并发。
常用调优配置
调整 worker_processes
指 Nginx 要生成的 worker 数量,最佳实践是每个 CPU 运行 1 个工作进程。
了解系统中的CPU核心数,输入
$ grep processor / proc / cpuinfo | wc -l
最大化 worker_connections
Nginx Web 服务器可以同时提供服务的客户端数。与 worker_processes
结合使用时,获得每秒可以服务的最大客户端数
最大客户端数/秒 = 工作进程 * 工作者连接数
为了最大化 Nginx 的全部潜力,应将工作者连接设置为核心一次可以运行的允许的最大进程数 1024。
启用Gzip压缩
压缩文件大小,减少了客户端 http 的传输带宽,因此提高了页面加载速度
建议的 gzip 配置示例如下:( 在http部分内)
为静态文件启用缓存
为静态文件启用缓存,以减少带宽并提高性能,可以添加下面的命令,限定计算机缓存网页的静态文件:
location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
}
Timeouts
keepalive 连接减少了打开和关闭连接所需的 CPU 和网络开销,获得最佳性能需要调整的变量可参考:
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;
禁用 access_logs
访问日志记录,它记录每个 nginx 请求,因此消耗了大量 CPU 资源,从而降低了 nginx 性能。
完全禁用访问日志记录:
access_log off;
如果必须具有访问日志记录,则启用访问日志缓冲:
access_log /var/log/nginx/access.log主缓冲区= 16k
Nginx 与 多进程模式 Apache 的比较
对于Apache,每个请求都会独占一个工作线程,当并发数到达几千时,就同时有几千的线程在处理请求了。这对于操作系统来说,占用的内存非常大,线程的上下文切换带来的 cpu 开销也很大,性能就难以上去,同时这些开销是完全没有意义的。
对于Nginx来讲,一个进程只有一个主线程,通过异步非阻塞的事件处理机制,实现了循环处理多个准备好的事件,从而实现轻量级和高并发。
事件驱动适合于 I/O 密集型服务,多进程或线程适合于 CPU 密集型服务:
- Nginx 更主要是作为反向代理,而非 Web 服务器使用。其模式是事件驱动。
- 事件驱动服务器,最适合做的就是这种 I/O 密集型工作,如反向代理,它在客户端与 WEB 服务器之间起一个数据中转作用,纯粹是 I/O 操作,自身并不涉及到复杂计算。因为进程在一个地方进行计算时,那么这个进程就不能处理其他事件了。
- Nginx 只需要少量进程配合事件驱动,几个进程跑 libevent,不像 Apache 多进程模型那样动辄数百的进程数。
- Nginx 处理静态文件效果也很好,那是因为读写文件和网络通信其实都是 I/O操作,处理过程一样。
参考文章