nginx处理请求的11个阶段
Nginx 处理请求的过程一共划分为 11 个阶段,按照执行顺序依次是 post-read、server-rewrite、find-config、rewrite、post-rewrite、preaccess、access、post-access、try-files、content 以及 log。

POST_READ 阶段:
POST_READ阶段是nginx处理请求流程中第一个可以添加模块函数的阶段,任何需要在接收完请求头之后立刻处理的逻辑可以在该阶段注册处理函数。nginx源码中只有realip模块在该阶段注册了函数,当nginx前端多了一个7层负载均衡层,并且客户端的真实ip被前端保存在请求头中时,该模块用来将客户端的ip替换为请求头中保存的值。realip模块之所以在POST_READ阶段执行的原因是它需要在其他模块执行之前悄悄的将客户端ip替换为真实值,而且它需要的信息仅仅只是请求头。一般很少有模块需要注册在POST_READ阶段,realip模块默认没有编译进nginx。
示例:
server {
listen 80;
server_name www.imcati.com;
set_real_ip_from 172.17.0.0/16;
real_ip_header x-real-ip;
location / {
set $addr $remote_addr;
return 200 "real-ip: $addr";
}
}
这里的配置是让 Nginx 把那些来自 172.17.0.0/16 网段的所有请求的来源地址,都改写为请求头 x-real-ip 所指定的值。同时该例使用了标准内建变量 $remote_addr 来输出当前请求的来源地址,以确认是否被成功改写。
本地curl确认是否进行修改: curl -H "x-real-ip: 114.114.114.114" http://www.imcati.com/

这里使用了 curl 工具的 -H 选项指定了额外的 HTTP 请求头 x-real-ip: 114.114.114.114. 从输出可以看到,$remote_addr 变量的值确实在 rewrite 阶段就已经成为了 x-real-ip 请求头中指定的值,即 114.114.114.114, 那么 Nginx 究竟是在什么时候改写了当前请求的来源地址呢?答案是:在 post-read 阶段。由于 rewrite 阶段的运行远在 post-read 阶段之后,所以当在 location 配置块中通过 set 配置指令读取 $remote_addr 内建变量时,读出的来源地址已经是经过 post-read 阶段篡改过的。
SERVER_REWRITE 阶段:
SERVER_REWRITE阶段是nginx中第一个必须经历的重要phase,请求进入此阶段时已经找到对应的虚拟主机(server)配置。nginx的rewrite模块在这个阶段注册了一个handler,rewrite模块提供url重写指令rewrite,变量设置指令set,以及逻辑控制指令if、break和return,用户可以在server配置里面,组合这些指令来满足自己的需求,而不需要另外写一个模块,比如将一些前缀满足特定模式的uri重定向到一个固定的url,还可以根据请求的属性来决定是否需要重写或者给用户发送特定的返回码。rewrite提供的逻辑控制指令能够满足一些简单的需求,针对一些较复杂的逻辑可能需要注册handler通过独立实现模块的方式来满足。
需要注意该阶段和后面的REWRITE阶段的区别,在SERVER_REWRITE阶段中,请求还未被匹配到一个具体的location中。该阶段执行的结果(比如改写后的uri)会影响后面FIND_CONFIG阶段的执行。另外这个阶段也是内部子请求执行的第一个阶段。
示例:
server {
listen 80;
server_name www.imcati.com;
location /test {
set $b "$a, world";
return 200 "args: $b";
}
set $a hello;
}
本地curl查看返回:curl http://www.imcati.com/test/

这里,配置语句 set $a hello 直接写在了 server 配置块中,因此它就运行在 server-rewrite 阶段,而 server-rewrite 阶段要早于 rewrite 阶段运行,因此写在 location 配置块中的语句 set $b "$a, world" 便晚于外面的 set $a hello 语句运行。
FIND_CONFIG 阶段:
FIND_CONFIG阶段顾名思义就是寻找配置阶段,具体一点就是根据uri查找location配置,实际上就是设置r->loc_conf,在此之前r->loc_conf使用的server级别的,查找location过程由函数ngx_http_core_find_location完成,值得注意的是当ngx_http_core_find_location函数返回NGX_DONE时,Nginx会返回301,将用户请求做一个重定向,这种情况仅发生在该location使用了proxy_pass/fastcgi/scgi/uwsgi/memcached模块,且location的名字以/符号结尾,并且请求的uri为该location除/之外的前缀,比如对location /xx/,如果某个请求/xx访问到该location,则会被重定向为/xx/。另外Nginx中location可以标识为internal,即内部location,这种location只能由子请求或者内部跳转访问。找到location配置后,Nginx调用了ngx_http_update_location_config函数来更新请求相关配置,其中最重要的是更新请求的content handler,不同location可以有自己的content handler。最后,由于有REWRITE_PHASE的存在,FIND_CONFIG阶段可能会被执行多次。
示例:
server {
listen 80;
server_name www.imcati.com;
location / {
return 200 "hello,world";
}
}
REWRITE 阶段:
REWRITE阶段为location级别的重写,这个阶段的checker和SERVER_REWRITE阶段的是同一个函数,而且Nginx的rewrite模块对这2个阶段注册的是同一个handler,2者唯一区别就是执行时机不一样,REWRITE阶段为location级别的重写,SERVER_REWRITE执行之后是FIND_CONFIG阶段,REWRITE阶段执行之后是POST_REWRITE阶段。
示例:
server {
listen 80;
server_name www.imcati.com;
location / {
root /usr/share/nginx/html;
rewrite . /rw/page break;
}
}
POST_REWRITE 阶段:
该阶段不能注册handler,仅仅只是检查上一阶段是否做了uri重写,如果没有重写的话,直接进入下一阶段;如果有重写的话,则利用next跳转域往前跳转到FIND_CONFIG阶段重新执行。Nginx对uri重写次数做了限制,默认是10次。
PREACCESS 阶段:
进入该阶段表明Nginx已经将请求确定到了某一个location(当该server没有任何location时,也可能是server),如论如何请求的loc_conf配置已经确定下来,该阶段一般用来做资源控制,默认情况下,诸如ngx_http_limit_conn_module,ngx_http_limit_req_module等模块会在该阶段注册handler,用于控制连接数,请求速率等。PREACCESS阶段使用的checker是默认的ngx_http_core_generic_phase函数。
示例:
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
listen 80;
server_name www.imcati.com;
location / {
limit_conn addr 1; //指定允许请求连接数
limit_rate 1k; //限定网络传输速率
root /usr/share/nginx/html;
}}
通过jmeter打压测试:
发送1个请求:

发送两个请求:

503是超过限制默认返回状态码。
ACCESS 阶段:
该阶段的首要目的是做权限控制,默认情况下,Nginx的ngx_http_access_module和ngx_http_auth_basic_module模块分别会在该阶段注册一个handler。
ACCESS阶段的checker是ngx_http_core_access_phase函数,此函数对handler返回值的处理大致和ngx_http_core_generic_phase一致,特殊的地方是当clcf->satisfy为NGX_HTTP_SATISFY_ALL,也就是需要满足该阶段注册的所有handler的验证时,某个handler返回NGX_OK时还需要继续处理本阶段的其他handler。clcf->satisfy的值可以使用satisfy指令指定。
示例:
server {
listen 80;
server_name www.imcati.com;
auth_basic "User Authentication";
auth_basic_user_file /etc/nginx/.passwd-www;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
添加认证文件:
htpasswd -c /etc/nginx/.passwd-www www ;
页面访问:

POST_ACCESS 阶段:
POST_ACCESS和POST_REWRITE阶段一样,只是处理一下上一阶段的结果,而不能挂载自己的handler,具体为如果ACCESS阶段返回了NGX_HTTP_FORBIDDEN或NGX_HTTP_UNAUTHORIZED(记录在r->access_code字段),该阶段会结束掉请求。
TRY_FILES 阶段:
TRY_FILES阶段仅当配置了try_files指令时生效,实际上该指令不常用,它的功能是指定一个或者多个文件或目录,最后一个参数可以指定为一个location或一个返回码,当设置了该指令时,TRY_FILES阶段调用checker函数ngx_http_core_try_files_phase来依此检查指定的文件或目录是否存在,如果本地文件系统存在某个文件或目录则退出该阶段继续执行下面的阶段,否则内部重定向到最后一个参数指定的location或返回指定的返回码。该阶段也不能注册handler。
示例:
server {
listen 80;
server_name www.imcati.com;
root /usr/share/nginx/html;
try_files $uri /index.php /index.html;
}
以上配置会按顺序检查文件是否存在,若存在则直接返回,顺序:$uri --> /index.php -->/index.html
查看返回:curl http://www.imcati.com/12345


CONTENT 阶段:
CONTENT阶段有些特殊,它不像其他阶段只能执行固定的handler链,还有一个特殊的content_handler,每个location可以有自己独立的content handler,而且当有content handler时,CONTENT阶段只会执行content handler,不再执行本阶段的handler链。
默认情况下,Nginx会在CONTENT阶段的handler链挂上index模块,静态文件处理模块等的handler。另外模块还可以设置独立的content handler,比如ngx_http_proxy_module的proxy_pass指令会设置一个名为ngx_http_proxy_handler的content handler。
CONTENT 阶段任务是生成响应内容并输出HTTP响应。如echo指令,echo_ecxec,proxy_pass,echo_location及content_by_lua都运行在此阶段。注意,与rewrite和access阶段不同,content阶段不同模块的配置指令不能一起混合使用。向 content 阶段注册配置指令本质上是在当前的 location 配置块中注册所谓的“内容处理程序”。而每一个 location 只能有一个"内容处理程序",因此,当在 location 中同时使用多个模块的 content 阶段指令时,只有其中一个模块能成功注册“内容处理程序”。所以应当避免在同一个 location 中使用多个模块的 content 阶段指令。
content阶段包含三个静态资源服务模块,ngx_index,ngx_autoindex,ngx_static用于当在location未使用任何content阶段的指令时处理URL请求。ngx_index和ngx_autoindex只作用于已/结尾的URI,其他由ngx_static执行。
ngx_index使用index指令用于查找首页文件,配合root指令实现,当找到文件后触发内部跳转而不是直接返回该文件,若都不存在则返回403。ngx_autoindex用于开启目录索引autoindex on,当index指定的首页文件不存在时返回该目录索引。
ngx_static处理所有的静态资源的请求, /VAR/WWW/目录下有index.html文件,如:
location / {
root /var/www/;
}
当请求index.html时,该location匹配上,并最终由ngx_static处理,返回该index.html。若没有root指定根目录,则使用安装nginx时使用的--prefix目录.
示例:
server {
listen 80;
server_name www.imcati.com;
root /usr/share/nginx/html;
location / {
index index.html;
}
}
查看返回:curl http://www.imcati.com/

LOG阶段
LOG阶段主要的目的就是记录访问日志,进入该阶段表明该请求的响应已经发送到系统发送缓冲区。另外这个阶段的handler链实际上并不是在ngx_http_core_run_phases函数中执行,而是在释放请求资源的ngx_http_free_request函数中运行,这样做的原因实际是为了简化流程,因为ngx_http_core_run_phases可能会执行多次,而LOG阶段只需要再请求所有逻辑都结束时运行一次,所以在ngx_http_free_request函数中运行LOG阶段的handler链是非常好的选择。
nginx处理请求的11个阶段的更多相关文章
- Nginx处理请求的11个阶段(agentzh的Nginx 教程学习记录)
Nginx 处理请求的过程一共划分为 11 个阶段,按照执行顺序依次是 post-read.server-rewrite.find-config.rewrite.post-rewrite.preacc ...
- 万字长文!一次性弄懂 Nginx 处理 HTTP 请求的 11 个阶段
Nginx 处理一个 HTTP 请求的全过程 前面给大家讲了 Nginx 是如何处理 HTTP请求头部的,接下来就到了真正处理 HTTP 请求的阶段了.先看下面这张图,这张图是 Nginx 处理 HT ...
- nginx系列7:处理HTTP请求的11个阶段
处理HTTP请求的11个阶段 如下图: 序号 阶段 指令 备注 1 POST_READ realip 获取客户端真实IP 2 SERVER_REWRITE rewrite 3 FIND_CONFIG ...
- Nginx 请求的11个阶段
48 1:当请求进入Nginx后先READ REQUEST HEADERS 读取头部 然后再分配由哪个指令操作 2:Identity 寻找匹配哪个Location 3:Apply Rate Limi ...
- nginx&http 第三章 ngx 请求处理的 11 个阶段 --ngx_http_process_request& ngx_http_handler
ngx_http_process_request如果设置了定时器则删除,既然所有的请求已经接收完毕,就不会再发生超时了 重设连接的读写回调函数 重设请求读事件回调函数 调用 ngx_http_hand ...
- 【Nginx】HTTP请求的11个处理阶段
Nginx将一个HTTP请求分成多个阶段.以模块为单位进行处理.这样做的优点是使处理过程更加灵活.减少耦合度.HTTP框架将处理分成了11个阶段,各个阶段能够包括随意多个HTTP模块并以流水线的方式处 ...
- nginx&http 第三章 ngx HTTP 请求的 11 个处理阶段
nginx 将一个 HTTP 请求分为 11 个处理阶段,这样做让每一个 HTTP 模块可以仅仅专注于完成一个独立.简单的功能,而一个请求的完整处理过程可以由多个 HTTP 模块共同合作完成将一次 H ...
- nginx的请求接收流程(一)
今年我们组计划写一本nginx模块开发以及原理解析方面的书,整本书是以open book的形式在网上会定时的更新,网址为http://tengine.taobao.org/book/index.htm ...
- atitit.提升开发效率---使用服务器控件生命周期 asp.net 11个阶段 java jsf 的6个阶段比较
atitit.提升开发效率---使用服务器控件生命周期 asp.net 11个阶段 java jsf 的6个阶段比较 如下列举了服务器控件生命周期所要经历的11个阶段. (1)初始化-- --在此 ...
随机推荐
- ajax 页面无刷新
<!-- 使用原生Ajax 和 $.ajax 实现局部刷新的过程 --><!-- 封装通用XMLHttpRequest对象 --><!DOCTYPE html>&l ...
- string::at
char& at (size_t pos); const char& at (size_t pos) const; #include <string>#include &l ...
- 2、python--第二天练习题
#1.有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中. #即: {'k ...
- re匹配 [\s\S][\w\W]的使用.
本来想提取一个字符串写了一堆正则都提取不出来. 因为有特殊字符 后来使用 [\s\S]* 或 [\w\W]* 匹配出来. \s 空白字符 [ \t\n\r\f\v] \S 非空白字符 相当于 [^ \ ...
- saltstack运维工具
salt介绍 saltstack是由thomas Hatch于2011年创建的一个开源项目,设计初衷是为了实现一个快速的远程执行系统. salt强大吗 系统管理员日常会进行大量的重复性操作,例如安装软 ...
- Smali基础知识
Smali是用于Dalvik(Android虚拟机)的反汇编程序实现 汇编工具(将Smali代码汇编为dex文件)为smali.jar baksmali.jar则是反汇编程序 地址:https://b ...
- JDK8 新特性 Lambda表达式
1.java8中Lambda表达式基础语法: (x,y) -> {} 左侧是一个小括号,里面是要实现的抽象方法的参数,有几个参数就写几个参数名,无参可写空括号,无需声明参数类型: 中间是一个jd ...
- [BJOI2019]奥术神杖——AC自动机+DP+分数规划+二分答案
题目链接: [BJOI2019]奥术神杖 答案是$ans=\sqrt[c]{\prod_{i=1}^{c}v_{i}}=(\prod_{i=1}^{c}v_{i})^{\frac{1}{c}}$. 这 ...
- Java并发指南5:JMM中的final关键字解析
本文转载自互联网,侵删 与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问.对于final域,编译器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的 ...
- Dart 语法中文在线学习网址收藏
为了学习flutter UI框架,必须先学好dart语言,故收藏了有关 Dart 语法中文在线学习网址 http://dart.goodev.org/guides/language/language- ...