Nginx请求处理流程
分析上图:
- Nginx大致会处理三种流量:WEB,EMAIL,TCP流量。
- Nginx中三个大的状态机:处理TCP、UDP的传输层状态机,处理应用层的HTTP状态机,处理邮件的MALL状态机。
- 当Nginx解析出请求需要访问静态资源的时候,会走左下方的流程,如果Nginx做反向代理的时候,将请求的资源缓存在磁盘也是这条线。
- 对于每个处理完成的请求,出记录Access日志,出错的情况下,还会记录Error日志,这会走右下方的流程。
- Nginx做反向代理或者负载均衡的时候,可以通过HTTP,MAIL即stream(TCP)等协议传输给后端服务器,
- 也可以通过应用层协议如FastCGI等代理到响应的应参层服务器,比如Nginx解析PHP文件请求的时候,会将请求通过FastCGI协议交给php应用程序处理。
Nginx进程结构
Nginx采用多进程结构,一个Master进程多个Worker进程。Nginx采用多进程而不是多线程最主要的原因是线程之间是共享地址空间的,如果地址空间出现错误如段错误时,会导致nginx线程全部挂掉,当使用多进程就不会出现这种问题,这就保证了nginx的高可用性。
Master进程用来管理Worker进程(如worker进程退出,master进程收到信号后,会重新启一个worker进程),Worker进程是用来真正处理请求的。Cache manager和Cache loader是为方向代理时,处理缓存后端资源,Cache loader用于缓存的载入,Cache manager用于缓存的管理。这些进程间的通信是由共享内存解决的。
为什么建议把Nginx的Worker进程数设置为CPU核数一样大小,并且把Worker进程和某个固定的内核绑定?
这是Nginx和Apache的一个不同之处,在Apache上每个进程在一个时刻只处理一个请求,因此,如果希望Web服务器拥有并发处理的请求数更多,就要把Apache的进程或线程数设置得更多,通常会达到一台服务器拥有几百个工作进程,这样大量的进程间切换将带来无谓的系统资源消耗。而Nginx则不然,一个worker进程可以同时处理的请求数只受限于内存大小,而且在架构设计上,不同的worker进程之间处理并发请求时几乎没有同步锁的限制,worker进程通常不会进入睡眠状态,因此把Nginx的Worker进程数设置为CPU核数一样大小,并且把Worker进程和某个固定的内核绑定时,进程间切换的代价是最小的。
还有一个原因就是可以更好地使用每颗CPU核上的缓存来降低缓存失效的命中率。
reload命令重载配置文件的步骤
- 向master进程发送HUP信号(相当于reload -s nginx命令)
- master进程检验配置语法是否正常
- master进程打开新的监听端口
- master进程用新配置启动新的worker子进程
- master进程向旧的worker子进程发送QUIT信号
- 旧的worker进程关闭监听句柄,处理完当前连接后结束进程(如果新的worker进程已经开始工作了,旧的worker进程长时间未退出怎么办?这种情况不会发生的,因为master进程会在新的worker进程开启之后给旧的worker进程设置一个定时器,当时间到了worker进程还未退出则立刻强制退出)
如何正确(优雅)地关闭worker进程
如果直接关闭worker进程,会导致已经连接的客户端收到错误响应。因此,正确的关闭worker进程的时候应该是worker进程没有处理请求的时候,如何做到?
当Nginx做TCP层反向代理的时候,它无法识别一个请求需要发送多少报文,所以这种类似的情况下是无法做到优雅关闭worker进程的。
优雅地关闭worker进程是相对于应用层服务HTTP请求而言的,流程如下:
- 设置定时器(worker_shutdown_timeout),表示进入优雅关闭worker进程的状态
- 关闭监听句柄,保证worker进程不会再去接收新的连接
- 关闭空闲连接。Nginx为了保证自己对资源的利用最大化,它会保存一个空闲的连接在连接池中,因此这一步会去连接池中关闭空闲的连接
- 在循环中等待全部连接关闭。轮询判断worker进程是否处理完了当前的任务,如果任务完成了或者还未完成任务但是已经超过定时器设置的时间的时候,会立即关闭worker进程
- 退出进程
Nginx事件驱动模型
事件驱动
事件驱动简单来说是指由一些事件发生源来产生事件,由一个或者多个事件收集器来收集、分发事件,然后许多事件处理器会注册自己感兴趣的事件,同时会“消耗”这些事件。
对于Nginx而言,一般会有网卡、磁盘产生事件。由Nginx的事件模块负责收集、分发操作,而所有的模块都可能是事件消费者,它们会向事件模块注册自己感兴趣的事件类型,这样,在有事件产生时,事件模块会把事件分发到相应的模块中进行处理。
- 当Nginx刚启动时,会打开80或者443端口等待连接事件,并且nginx处于sleep状态。这一步对应epoll中的epool_wait方法
- 当操作系统收到TCP三次握手流程并处理完后,操作系统就会唤醒nginx流程继续往下走
- 上图中的kernel表示操作系统内核,nginx需要向kernel取事件。kernel会把准备好的事件放到事件队列中,nginx在事件队列中取出事件,比如建立连接、收到TCP请求报文。
请求切换的比较
蓝色、蓝色、橙色分别代表请求1,请求2,请求3,左边三个矩形分别代表进程1,进程2,进程2,绿色箭头表示切换。
传统服务器如Apache
如上图左边,当Process1在处理请求1的时候,,如果遇到网络事件不满足的时候,就会去Process2处理请求2,可能过一会由于某种原因又会切换到Process3处理请求3,可以看到这种方式会发生频繁的进程间切换。
Nginx
如上图右边,当进程1不满足某些事件的时候,直接在用户态就切换到了下一个请求处理流程。