Nginx本身不会对PHP进行解析,终端对PHP页面的请求将会被Nginx交给FastCGI进程监听的IP地址及端口,由php-fpm作为动态解析服务器处理,最后将处理结果再返回给nginx。其实,Nginx就是一个反向代理服务器。Nginx通过反向代理功能将动态请求转向后端php-fpm,从而实现对PHP的解析支持,这就是Nginx实现PHP动态解析的原理。
Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接收到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端。
经典的模型就是Nginx中所用的Master-Worker多进程异步驱动模型。
父进程创建socket,bind、listen后,通过fork创建多个子进程,每个子进程继承了父进程的socket,调用accpet开始监听等待网络连接。这个时候有多个进程同时等待网络的连接事件,当这个事件发生时,这些进程被同时唤醒,就是“惊群”。进程被唤醒,需要进行内核重新调度,这样每个进程同时去响应这一个事件,而最终只有一个进程能处理事件成功,其他的进程在处理该事件失败后重新休眠或其他。
其实在Linux2.6版本以后,内核内核已经解决了accept()函数的“惊群”问题,当内核接收到一个客户连接后,只会唤醒等待队列上的第一个进程或线程。
Nginx中使用accept_mutexmutex互斥锁解决这个问题,具体措施有使用全局互斥锁,每个子进程在epoll_wait()之前先去申请锁,申请到则继续处理,获取不到则等待,并设置了一个负载均衡的算法(当某一个子进程的任务量达到总设置量的7/8时,则不会再尝试去申请锁)来均衡各个进程的任务量。
现在我们对惊群及 Nginx 的处理总结如下:
accept 不会有惊群,epoll_wait 才会。
Nginx 的 accept_mutex,并不是解决 accept 惊群问题,而是解决 epoll_wait 惊群问题。
说Nginx 解决了 epoll_wait 惊群问题,也是不对的,它只是控制是否将监听套接字加入到epoll 中。监听套接字只在一个子进程的 epoll 中,当新的连接来到时,其他子进程当然不会惊醒了。
简单了说,就是同一时刻只允许一个nginx worker在自己的epoll中处理监听句柄。它的负载均衡也很简单,当达到最大connection的7/8时,本worker不会去试图拿accept锁,也不会去处理新连接,这样其他nginx worker进程就更有机会去处理监听句柄,建立新连接了。而且,由于timeout的设定,使得没有拿到锁的worker进程,去拿锁的频繁更高。
nginx 多进程模型真的没有锁了吗?其实还是有一个的:ngx_accept_mutex。
nginx是一个多进程程序,80端口为各worker进程共享,每当有连接到来时,势必多个worker进程都要争着去响应,这也就是所谓的惊群现象。
当内核accept一个链接时,会唤醒所有等待中的进程,但实际上只有一个进程能获取连接,其它的进程都被无效唤醒,这种无效唤醒无疑将会增加应用的开销。为此,nginx提供了一把accept锁避免九子夺嫡的悲剧。
ngx_accept_mutex的作用也就是让那些当前负载严重
的worker进程主动放弃对新到来的请求的处理,提高
应用整体的唤醒效率,进而提升应用的整体性能。
proxy_cache
upstream
fastcgi_pass
location
非标准状态码444表示关闭连接且不给客户端发响应头。
nginx -s reload 命令加载修改后的配置文件,命令下达后发生如下事件
1. Nginx的master进程检查配置文件的正确性,若是错误则返回错误信息,nginx继续采用原配置文件进行工作(因为worker未受到影响)
2. Nginx启动新的worker进程,采用新的配置文件
3. Nginx将新的请求分配新的worker进程
4. Nginx等待以前的worker进程的全部请求已经都返回后,关闭相关worker进程
5. 重复上面过程,知道全部旧的worker进程都被关闭掉
以上过程是参考nginx官方的相关文档后得出。
以 proxy_next_upstream为例, 一般的配置如下:
proxy_next_upstream http_504 timeout;
这个指令有两个作用 :
告诉nginx,如果发生了连接超时,上游返回504的时候,需要重试upstream
告诉nginx, http 504 ,连接超时 是 请求失败
总的来说, nginx对于 error, timeout, invalide_header 均默认是失败, 其他的行为如果想当作失败,需要加入类似 proxy_next_upstream之类的指令里面的 。 其中 http_403, http_404均不认识是失败。
论两点, 对 server 失败的定义 和 server失败后的行为。
server 失败的定义 : 上面的模块失败定义主要用于说明一个请求在什么情况下是失败的;但是什么定义一个server是失败的呢? nginx里面主要用两个参数控制 : max_fails和fail_timeout。 简单的说,如果一个请求在 fail_timeout内发生了max_fails次失败,就认识改server当前是down。
失败后的行为 : 主要为:
在 fail_timeout 时间段,都不会选择该server ;
在 fail_timeout 时间后,会将该server标记为 normal,重复已有的逻辑。
Ngxin把对客户端请求的处理过程划分为11个阶段
#1 NGX_HTTP_POST_READ_PHASE: 读取请求内容阶段
#2 NGX_HTTP_SERVER_REWRITE_PHASE: Server请求地址重写阶段
#3 NGX_HTTP_FIND_CONFIG_PHASE: 配置查找阶段
#4 NGX_HTTP_REWRITE_PHASE: Location请求地址重写阶段
#5 NGX_HTTP_POST_REWRITE_PHASE: 请求地址重写提交阶段
#6 NGX_HTTP_PREACCESS_PHASE: 访问权限检查准备阶段
#7 NGX_HTTP_ACCESS_PHASE: 访问权限检查阶段
#8 NGX_HTTP_POST_ACCESS_PHASE: 访问权限检查提交阶段
#9 NGX_HTTP_TRY_FILES_PHASE: 配置项try_files处理阶段
#10 NGX_HTTP_CONTENT_PHASE: 内容产生阶段
#11 NGX_HTTP_LOG_PHASE: 日志模块处理阶段
Nginx的请求处理流程
#1 Nginx如何确认由哪个server处理该请求?
1. 利用ip + port 确认监听该ip和端口的server。
2. 根据请求中的host首部确认选择那个server处理该请求。
3. 如果没有匹配到任何server,则把该请求转给默认(default)server处理,
一般而言,不加任何设置的话,配置文件中顺序出现的第一个server作为
default server。
4. 可以对listen指令使用default_server标志,去设置某个server为
default server。
#2 Nginx如何根据host首部匹配server?
Nginx主要是通过比较server中的server_name和host首部来匹配server的。
比较顺序如下所示:
1. 精确的name;
2. 最长匹配的前导通配符name(如:*.zhidao.baidu.com);
3. 最长的后导通配符name(如:zhidao.baidu.*);
#3 初始化http请求,http请求的11个阶段
location匹配命令
~ 波浪线表示执行一个正则匹配,区分大小写。
~* 表示执行一个正则匹配,不区分大小写。
^~ ^~表示普通字符匹配,如果该选项匹配,只匹配该选项,不匹配别的选项,一般用来匹配目录。
= 进行普通字符精确匹配。
@ "@" 定义一个命名的 location,使用在内部定向时,例如 error_page,try_files。
示例:
请求URI例子:
/ -> 符合configuration A
/documents/document.html -> 符合configuration B
/images/1.gif -> 符合configuration C
/documents/1.jpg ->符合 configuration D
相关推荐:
以上是nginx相关知识点总结分享的详细内容。更多信息请关注PHP中文网其他相关文章!