Nginx 高并发高性能实现

Nginx 高并发实现模型

Nginx 采用的是多进程(单线程)和多路IO复用模型。使用了 I/O 多路复用技术的 Nginx,就成了“并发事件驱动”的服务器。

Nginx 在启动后,会有一个 master 进程和多个相互独立的 worker 进程。master 接收来自外界的信号,向各 worker 进程发送信号,每个进程都有可能来处理这个连接。同时,master 进程能监控 worker 进程的运行状态,当 worker 进程退出后(异常情况下),会自动启动新的 worker 进程。

orker 进程数,一般会设置成机器 cpu 核数。因为更多的 worker 数,只会导致进程相互竞争 cpu,从而带来不必要的上下文切换。

使用多进程模式,不仅能提高并发率,而且进程之间相互独立,一个 worker 进程挂了不会影响到其他 worker 进程。

Nginx master和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部分内)

nginx中gzip配置

为静态文件启用缓存

为静态文件启用缓存,以减少带宽并提高性能,可以添加下面的命令,限定计算机缓存网页的静态文件:

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 密集型服务:

  1. Nginx 更主要是作为反向代理,而非 Web 服务器使用。其模式是事件驱动。
  2. 事件驱动服务器,最适合做的就是这种 I/O 密集型工作,如反向代理,它在客户端与 WEB 服务器之间起一个数据中转作用,纯粹是 I/O 操作,自身并不涉及到复杂计算。因为进程在一个地方进行计算时,那么这个进程就不能处理其他事件了。
  3. Nginx 只需要少量进程配合事件驱动,几个进程跑 libevent,不像 Apache 多进程模型那样动辄数百的进程数。
  4. Nginx 处理静态文件效果也很好,那是因为读写文件和网络通信其实都是 I/O操作,处理过程一样。

参考文章

Nginx 是如何实现高并发?常见的优化手段有哪些?
nginx实现高并发的原理
nginx高并发是如何设计