您的位置:首页 > 运维架构 > Nginx

Nginx是如何处理每一个请求的

2017-09-12 21:51 435 查看

Nginx架构

nginx运行时,在unix系统中以daemon形式在后台运行,后台进程包含一个master进程和多个worker进程。Nginx以多进程形式工作,也支持多线程方式,但nginx默认采用多进程方式,也是主流方式。

Nginx多进程模式

多进程模式,会有一个master进程和多个worke进程 

Master进程管理worker进程,包括: 

接收来自外界的信号; 

向各个worker进程发送信号; 

监控worker进程状态; 

当worker退出后(异常情况下),自动重新启动新的worker进程

多个worker进程之间对等,竞争来自客户端的请求,一个请求,只会在一个worker中处理,一个worker进程不会处理其他进程的请求.worker进程个数的设置,一般与机器的CPU核数一致.

多进程模式的好处: 

每个worker进程相互独立,无需加锁,节省锁开销; 

采用独立的进程,不会相互影响,一个进程退出,其他进程服务不会中断; 

Worker异常退出,会导致当前worker上的所有请求失败,不过不会影响所有请求,降低了风险。

多进程模式对并发的支持 

每个worker只有一个主线程,采用异步非阻塞方式来处理请求,使得nginx可以同时处理成千上万个请求。相比Apache,每个请求会独占一个工作线程,并发上千时,就同时有几千的线程在处理请求,线程带来的内存占用很大,线程的上下切换带来的cpu开销也大,性能就上不去了。

什么是异步非阻塞

一个请求的完整过程:请求过来,建立连接,然后接收数据,接收数据后,再发送数据。 

具体到系统底层,就是读写事件,当读写事件没有准备好时,如果不用非阻塞的方式来调用,就得阻塞调用了,事件没准备好,就只能等,等事件准备好再继续。阻塞调用会进入内核等待,让出cpu,对单线程的worker来说,显然不合适,当网络事件越多时,等待很多,cpu利用率上不去。非阻塞就是,事件没有准备好,马上返回eagain,表示事件还没准备好,过会儿再来,过一会,再来检查一下事件,直到事件准备好为止,在这期间,你可以先去做其他事情,然后再来看看事件好了没。这时,虽不阻塞了,但是还得不时来检查事件的状态,带来的开销也不小。所以有了异步非阻塞的事件处理机制,具体到系统调用就是像
select/poll/epoll/kquene这样的系统调用。提供一种机制,让你可以同时监控多个事件,调用他们是阻塞的,但是可以设置超时时间,在超时时间之内,如果有事件准备好了就返回。这种机制解决了上面的两个问题,以epoll为例,当事假没准备好时,放到epoll里,事件准备好了,就去读写,当读写返回eagain时,将它再次加入epoll,这样,只要有事件准备好了,就去处理它,只有当所有事件都没有准备好时,才在epoll里等着。这样,就可以支持大量的并发,这里的并发请求,是指未处理完的请求,线程只有一个,同时处理的请求只有一个,只是在请求间不断切换,切换是因为异步事件未准备好,主动让出的。这里的切换没有什么代价,可以理解为在循环处理多个准备好的事件,事实上也是。与多线程相比,这种事件处理方式有很大优势,不需创建线程,每个请求占用的内存也很少,没有上下文切换,事件处理非常轻量级,没有上下文切换的开销,更多并发,只会占更多的内存而已。现在的网络服务器基本都采用这种方式,也是nginx性能高效的主要原因。

操作nginx

master进程会接收来自外界发来的信号,因此要控制nginx,通过kill向master进程发送信号就可以了。如 
kill –HUP pid
,重启nginx,或重新加载配置,而不中断服务。Master进程在接到这个信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发信号,不再接收新的请求,并且在处理完所有未处理完的请求后,退出。新的worker启动后,就开始接收新的请求。

直接给master发信号,是比较老的操作方法,在nginx0.8版本后,可以使用命令行参数,方便管理,如./nginx –s reload ,重启nginx; ./nginx –s stop,停止nginx。这种方式的内部原理是,执行命令时,会启动一个新的nginx进程,该进程在解析到reload参数后,知道目标是控制nginx重新加载配置文件,它会向master进程发送信号,接下来的处理,和直接向master进程发送信号一样。

Nginx处理请求

worker进程是如何处理请求的: 

一个连接请求过来,每个进程都有可能处理这个连接.worker进程是从master进程fork出来的,在master进程里,先建立好需要listen的socket(listenfd)后,然后再fork出多个worker进程.所有worker进程的listenfd会在新连接来时变得可读,为了保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接.当一个worker进程在accept这个连接之后,开始读取请求,解析请求,产生数据后,再返回客户端,最后才断开连接,这就是一个完整的请求处理.一个请求,完全由worker处理,且只在一个worker里处理.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  nginx