Nginx原理详情个人理解
Posted 狗哥狗弟齐头并进
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx原理详情个人理解相关的知识,希望对你有一定的参考价值。
1 Master进程的作用?
负责读取并验证Nginx.conf,管理worker进程。
2 Worker进程的作用?
每一个worker进程都维护一个线程,用来处理请求。Worker的数量与cpu核心数有关,有利于线程的切换。
3 Nginx如何做到热部署
所谓的热部署就是在修改了配置文件的情况下,不需要重启Nginx就可以用新得配置文件去处理请求。Nginx中在修改完配置文件之后,会重新生成新得worker,至于旧的worker,当他们处理完旧的请求就kill掉了。
4 Nginx如何做到高并发下的高效处理。
Nginx采用了Linux的epoll模型,epoll模型基于时间驱动机制,它可以监控多个事件是否准备完成,如果ok,那么放入epoll队列中,这个过程是异步的,worker只需要从epoll队列中循环处理就可以了。
5 Nginx挂了怎么办?
Nginx既然作为入口网关,如果出现单点问题,显然是不能接受的。
保证Nginx不挂采用keepalived +Nginx实现高可用。Keepalived是一个高可用解决方法,主要用来防止服务器单点发生故障
6 Nginx的模型相关?
Nginx采用多线程+异步非阻塞模型去处理请求。Nginx真正处理请求业务的是Worker下的线程。Worker进程中有一个很关键的函数是nginx_worker_process_cycle,是一个无限循环,不断处理收到来自客户端的请求,并进行处理。知道Nginx的服务停止。
在这个函数中一个请求的简单处理流程为:
① 操作系统提供的epoll机制获取相关的事件。
② 接受和处理这些事件。如果接受的是事件,就去响应。如果接受的是数据,那么就会产生一个request对象。
③ 设置request的header和body。
④ 返回响应数据给客户端。
⑤ 完成request的处理。
⑥ 重新初始化定时器及其他事件。
7 Nginx中的多进程处理请求的方式。(多线程+异步非阻塞I/0)
首先,master进程一开始根据我们的配置,来建立需要listen的网络socket fd,然后fork出多个worker进程。其次,根据进程的特性,新建立的worker进程也和master进程一样具有相同的设置。因此,也会去监听相同ip端口的套接字(socket fd)。然后当这个时候有多个worker进程监听到事件的时候,就会去处理。那么怎么去处理多个woker处理同一个请求的情况呢?为了保证只会有一个进程成功的处理事件,那么Nginx中实现了一个accept_mutex 类似互斥锁,只有获取到这个锁的进程,才能去注册读事件。
最后,监听成功的worker才会去读取请求,解析处理,响应数据返回给客户端,断开连接,结束。因此,一个request请求,只需要worker进程就可以完成。
这种模型 的好处就是:各个进程是独立的,当一个worker出现异常不会影响其他。此外,独立进程也会避免一些不需要的锁操作,这样子会提高效率。
8 Nginx怎么去控制多进程之间的连接平衡。
背景:Worker进程会京城监听客户端的请求,回出现可能所有的请求都被一个worker进程给竞争获取了,导致了其他进程都比较空闲,而某一个进程会处于忙碌的状态。这种状态可能还会导致无法及时响应,失去能力。
Nginx采用了一种是否打开nginx_mutex互斥锁的值的选项。Nginx中用nginx_accept_disabled标识控制一个worker进程是否需要去竞争获取accepy_mutex
.进而去获取事件。
Nignx_accept_disabled = nginx单进程的所有连接的八分之一 减去 空闲连接。
所以当空闲连接变小,意味着我这个worker获取了很多的连接。那么此时,就不想让他再获取更多的连接了,nginx的处理是,只要这个值大于0 就不会去尝试获取accept_mutex锁。并且每次运行到这里都会-1 .所以当空闲连接变小的时候,那么就不会去竞争。此时其他进程获取锁的机会就大了。 这样就实现了多进程之间的连接平衡了。
9 Nginx处理一个简单的http请求。
对于Nginx监听到的客户端连接,都会将它的读事件的handler设置为ngx_http_init_request 函数,这个函数就是请求处理的入口。
请求进来之后,就会去处理请头,请求体,并且解析请求头请求体。处理完之后基本上就是handler 各个阶段的handler去处理请求。在某个阶段依次调用handler。产生响应。
为了给request一个正确的响应,Nginx必须把这个request交给一个合适的content handler。然后去location块去处理,比如proxy_pass 代理到某个路径之上。如果没有合适的,那么可能去执行一些默认的 比如index页面这些。内容产生完毕之后,就去各个过滤模块去过滤了。
10 keepalive长连接。
什么是长连接呢?我们知道,http请求是基于TCP协议之上的,那么,当客户端在发起请求前,需要先与服务端建立TCP连接,而每一次的TCP连接是需要三次握手来确定的,如果客户端与服务端之间网络差一点,这三次交互消费的时间会比较多,而且三次交互也会带来网络流量。当然,当连接断开后,也会有四次的交互,当然对用户体验来说就不重要了。而http请求是请求应答式的,如果我们能知道每个请求头与响应体的长度,那么我们是可以在一个连接上面执行多个请求的,这就是所谓的长连接,但前提条件是我们先得确定请求头与响应体的长度。
对于请求来说,如果当前请求需要有body,如POST请求,那么nginx就需要客户端在请求头中指定content-length来表明body的大小,否则返回400错误。也就是说,请求体的长度是确定的,那么响应体的长度呢?先来看看http协议中关于响应body长度的确定:
对于http1.0协议来说,如果响应头中有content-length头,则以content-length的长度就可以知道body的长度了,客户端在接收body时,就可以依照这个长度来接收数据,接收完后,就表示这个请求完成了。而如果没有content-length头,则客户端会一直接收数据,直到服务端主动断开连接,才表示body接收完了。 而对于http1.1协议来说,如果响应头中的Transfer-encoding为chunked传输,则表示body是流式输出,body会被分成多个块,每块的开始会标识出当前块的长度,此时,body不需要通过长度来指定。如果是非chunked传输,而且有content-length,则按照content-length来接收数据。否则,如果是非chunked,并且没有content-length,则客户端接收数据,直到服务端主动断开连接。
从上面,我们可以看到,除了http1.0不带content-length以及http1.1非chunked不带content-length外,body的长度是可知的。此时,当服务端在输出完body之后,会可以考虑使用长连接。能否使用长连接,也是有条件限制的。如果客户端的请求头中的connection为close,则表示客户端需要关掉长连接,如果为keep-alive,则客户端需要打开长连接,如果客户端的请求中没有connection这个头,那么根据协议,如果是http1.0,则默认为close,如果是http1.1,则默认为keep-alive。如果结果为keepalive,那么,nginx在输出完响应体后,会设置当前连接的keepalive属性,然后等待客户端下一次请求。
当然,nginx不可能一直等待下去,如果客户端一直不发数据过来,岂不是一直占用这个连接?所以当nginx设置了keepalive等待下一次的请求时,同时也会设置一个最大等待时间,这个时间是通过选项keepalive_timeout来配置的,如果配置为0,则表示关掉keepalive,此时,http版本无论是1.1还是1.0,客户端的connection不管是close还是keepalive,都会强制为close。
如果服务端最后的决定是keepalive打开,那么在响应的http头里面,也会包含有connection头域,其值是”Keep-Alive”,否则就是”Close”。如果connection值为close,那么在nginx响应完数据后,会主动关掉连接。所以,对于请求量比较大的nginx来说,关掉keepalive最后会产生比较多的time-wait状态的socket。一般来说,当客户端的一次访问,需要多次访问同一个server时,打开keepalive的优势非常大,比如图片服务器,通常一个网页会包含很多个图片。打开keepalive也会大量减少time-wait的数量。
参考:https://cloud.tencent.com/developer/article/1427219
以上是关于Nginx原理详情个人理解的主要内容,如果未能解决你的问题,请参考以下文章