Nginx 为何快到停不下来?

来自:NingG 我的博客

连接:http://ningg.top/nginx-series-principle/html

Nginx 的进程模型


Nginx 服务器,正常运行过程当中:nginx

  1. 多进程:一个 Master 进程、多个 Worker 进程算法

  2. Master 进程:管理 Worker 进程apache

  3. 对外接口:接收外部的操做(信号)后端

  4. 对内转发:根据外部的操做的不一样,经过信号管理 Worker数组

  5. 监控:监控 worker 进程的运行状态,worker 进程异常终止后,自动重启 worker 进程服务器

  6. Worker 进程:全部 Worker 进程都是平等的网络

  7. 实际处理:网络请求,由 Worker 进程处理;多线程

  8. Worker 进程数量:在 nginx.conf 中配置,通常设置为核心数,充分利用 CPU 资源,同时,避免进程数量过多,避免进程竞争 CPU 资源,增长上下文切换的损耗。并发

思考:

  1. 请求是链接到 Nginx,Master 进程负责处理和转发?

  2. 如何选定哪一个 Worker 进程处理请求?请求的处理结果,是否还要通过 Master 进程?

HTTP 链接创建和请求处理过程

  1. Nginx 启动时,Master 进程,加载配置文件

  2. Master 进程,初始化监听的 socket

  3. Master 进程,fork 出多个 Worker 进程

  4. Worker 进程,竞争新的链接,获胜方经过三次握手,创建 Socket 链接,并处理请求

Nginx 高性能、高并发

  1. Nginx 采用:多进程 + 异步非阻塞方式(IO 多路复用 epoll)

  2. 请求的完整过程:

  3. 创建链接

  4. 读取请求:解析请求

  5. 处理请求

  6. 响应请求

  7. 请求的完整过程,对应到底层,就是:读写 socket 事件

Nginx 的事件处理模型

request:Nginx 中 http 请求。

基本的 HTTP Web Server 工做模式:

  1. 接收请求:逐行读取请求行和请求头,判断段有请求体后,读取请求体

  2. 处理请求

  3. 返回响应:根据处理结果,生成相应的 HTTP 请求(响应行、响应头、响应体)

Nginx 也是这个套路,总体流程一致。

模块化体系结构

nginx的模块根据其功能基本上能够分为如下几种类型:

  • event module: 搭建了独立于操做系统的事件处理机制的框架,及提供了各具体事件的处理。包括ngx_events_module, ngx_event_core_module和ngx_epoll_module等。nginx具体使用何种事件处理模块,这依赖于具体的操做系统和编译选项。

  • phase handler: 此类型的模块也被直接称为handler模块。主要负责处理客户端请求并产生待响应内容,好比ngx_http_static_module模块,负责客户端的静态页面请求处理并将对应的磁盘文件准备为响应内容输出。

  • output filter: 也称为filter模块,主要是负责对输出的内容进行处理,能够对输出进行修改。例如,能够实现对输出的全部html页面增长预约义的footbar一类的工做,或者对输出的图片的URL进行替换之类的工做。

  • upstream: upstream模块实现反向代理的功能,将真正的请求转发到后端服务器上,并从后端服务器上读取响应,发回客户端。upstream模块是一种特殊的handler,只不过响应内容不是真正由本身产生的,而是从后端服务器上读取的。

  • load-balancer: 负载均衡模块,实现特定的算法,在众多的后端服务器中,选择一个服务器出来做为某个请求的转发服务器。

常见问题剖析

Nginx vs. Apache

网络 IO 模型:

  1. nginx:IO 多路复用,epoll(freebsd 上是 kqueue )

  2. 高性能

  3. 高并发

  4. 占用系统资源少

  5. apache:阻塞 + 多进程/多线程

  6. 更稳定,bug 少

  7. 模块更丰富

场景:

处理多个请求时,能够采用:IO 多路复用 或者 阻塞 IO +多线程

  • IO 多路服用:一个 线程,跟踪多个 socket 状态,哪一个就绪,就读写哪一个;

  • 阻塞 IO + 多线程:每个请求,新建一个服务线程

思考:IO 多路复用 和 多线程 的适用场景?

  • IO 多路复用:单个链接的请求处理速度没有优点,适合 IO 密集型 场景,事件驱动

  • 大并发量:只使用一个线程,处理大量的并发请求,下降上下文环境切换损耗,也不须要考虑并发问题,相对能够处理更多的请求;

  • 消耗更少的系统资源(不须要线程调度开销)

  • 适用于长链接的状况(多线程模式长链接容易形成线程过多,形成频繁调度)
    阻塞IO + 多线程:实现简单,能够不依赖系统调用,适合 CPU 密集型 场景
    每一个线程,都须要时间和空间;

  • 线程数量增加时,线程调度开销指数增加

Nginx 最大链接数

基础背景:

  1. Nginx 是多进程模型,Worker 进程用于处理请求;

  2. 单个进程的链接数(文件描述符 fd),有上限(nofile):ulimit -n

  3. Nginx 上配置单个 worker 进程的最大链接数:worker_connections 上限为 nofile

  4. Nginx 上配置 worker 进程的数量:worker_processes

所以,Nginx 的最大链接数:

  1. Nginx 的最大链接数:Worker 进程数量 x 单个 Worker 进程的最大链接数

  2. 上面是 Nginx 做为通用服务器时,最大的链接数

  3. Nginx 做为反向代理服务器时,可以服务的最大链接数:(Worker 进程数量 x 单个 Worker 进程的最大链接数)/ 2。

  4. Nginx 反向代理时,会创建 Client 的链接和后端 Web Server 的链接,占用 2 个链接

思考

  • 每打开一个 socket 占用一个 fd

  • 为何,一个进程可以打开的 fd 数量有限制?

IO 模型

场景:

处理多个请求时,能够采用:IO 多路复用 或者 阻塞 IO +多线程

  • IO 多路复用:一个 线程,跟踪多个 socket 状态,哪一个就绪,就读写哪一个;

  • 阻塞 IO + 多线程:每个请求,新建一个服务线程

思考:IO 多路复用 和 多线程 的适用场景?

  • IO 多路复用:单个链接的请求处理速度没有优点

  • 大并发量:只使用一个线程,处理大量的并发请求,下降上下文环境切换损耗,也不须要考虑并发问题,相对能够处理更多的请求;

  • 消耗更少的系统资源(不须要线程调度开销)

  • 适用于长链接的状况(多线程模式长链接容易形成线程过多,形成频繁调度)

  • 阻塞IO + 多线程:实现简单,能够不依赖系统调用。

  • 每一个线程,都须要时间和空间;

  • 线程数量增加时,线程调度开销指数增加

select/poll 和 epoll 比较

详细内容,参考:

  • select poll epoll三者之间的比较

select/poll 系统调用

// select 系统调用
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
// poll 系统调用
int poll(struct pollfd fds[], nfds_t nfds, int timeout);

select

  • 查询 fd_set 中,是否有就绪的 fd,能够设定一个超时时间,当有 fd (File descripter) 就绪或超时返回;

  • fd_set 是一个位集合,大小是在编译内核时的常量,默认大小为 1024

  • 特色:

  • 链接数限制,fd_set 可表示的 fd 数量过小了;

  • 线性扫描:判断 fd 是否就绪,须要遍历一边 fd_set;

  • 数据复制:用户空间和内核空间,复制链接就绪状态信息

poll

  • 解决了链接数限制:

  • poll 中将 select 中的 fd_set 替换成了一个 pollfd 数组

  • 解决 fd 数量太小的问题

  • 数据复制:用户空间和内核空间,复制链接就绪状态信息

  • epoll:event 事件驱动

epoll:event 事件驱动

  • 事件机制:避免线性扫描

  • 为每一个 fd,注册一个监听事件

  • fd 变动为就绪时,将 fd 添加到就绪链表

  • fd 数量:无限制(OS 级别的限制,单个进程能打开多少个 fd)

select,poll,epoll

  1. I/O多路复用的机制;

  2. I/O多路复用就经过一种机制,能够监视多个描述符,一旦某个描述符就绪(通常是读就绪或者写就绪),可以通知程序进行相应的读写操做。

  3. 监视多个文件描述符

  4. 但select,poll,epoll本质上都是同步I/O:

  5. 用户进程负责读写(从内核空间拷贝到用户空间),读写过程当中,用户进程是阻塞的;

  6. 异步 IO,无需用户进程负责读写,异步IO,会负责从内核空间拷贝到用户空间;

Nginx 的并发处理能力

关于 Nginx 的并发处理能力:

  • 并发链接数,通常优化后,峰值能保持在 1~3w 左右。(内存和 CPU 核心数不一样,会有进一步优化空间)