Wenn eine Anfrage von Nginx verarbeitet wird, durchläuft sie eine Reihe von Phasen (Phasen). optionale Exit-Methoden für jede Phase, enthaltene Module und entsprechende Anweisungen
phase | optional exits | modules / directives | description |
NGX_HTTP_POST_READ_PHASE | HttpRealIpModule | 读取请求内容阶段 | |
NGX_HTTP_SERVER_REWRITE_PHASE (server rewrite) | HttpRewriteModule / rewrite | 请求地址重写阶段 | |
NGX_HTTP_FIND_CONFIG_PHASE(location selection) | HttpCoreModule / location | 配置查找阶段 | |
NGX_HTTP_REWRITE_PHASE Location(location rewrite) | location selection, finalize request |
HttpRewriteModule / rewrite | 请求地址重写阶段 |
NGX_HTTP_POST_REWRITE_PHASE | HttpRewriteModule / rewrite | 请求地址重写提交阶段 | |
NGX_HTTP_PREACCESS_PHASE | degradation, NginxHttpLimitZoneModule / limit_zone, HttpLimitReqModule / limit req, HttpRealIpModule |
访问权限检查准备阶段 | |
NGX_HTTP_ACCESS_PHASE | finalize request | HttpAccessModule / allow, deny, NginxHttpAuthBasicModule / auth_basic | 访问权限检查阶段 |
NGX_HTTP_POST_ACCESS_PHASE | 访问权限检查提交阶段 | ||
NGX_HTTP_TRY_FILES_PHASE | location selection | HttpCoreModule / try_files | 配置项try_files处理阶段 |
NGX_HTTP_CONTENT_PHASE | HttpAutoindexModule / autoindex,HttpCoreModule / Core, HttpDavModule / DAV, HttpEmptyGifModule / EmptyGif, HttpFcgiModule / FastCGI, HttpFlvStreamModul / FLV, HttpGzipStaticModule / gzip_static, HttpIndexModule / index, HttpMemcachedModule / memcached, EmbeddedPerlModule / perl, HttpProxyModule / proxy, HttpProxyModule / random_index, HttpScgiModule / scgi, HttpStubStatusModule / stub_status, HttpUwsgiModule / uwsgi HttpLuaModule / content_by_lua, HttpCoreModule / proxy_pass |
内容产生阶段 | |
NGX_HTTP_LOG_PHASE | HttpLogModuel / access_log | 日志模块处理阶段 |
Nach dem Lesen des Anforderungsheaders geht es in die Post-Read-Phase. Diese Phase ermöglicht es Nginx, den Wert der IP-Adresse im Anforderungsheader zu ändern. Das zugehörige Modul HttpRealIpModule.
In dieser Phase geht es hauptsächlich um die Initialisierung globaler Variablen oder das Umschreiben auf Serverebene. Wenn der Rewrite-Befehl auf dem Server platziert wird, wird die Server-Rewrite-Phase gestartet. (Anweisungen zum Umschreiben finden Sie in der Umschreibephase)
Diese Phase verwendet die umgeschriebene URL, um den entsprechenden Speicherort zu finden Beachten Sie, dass diese Phase möglicherweise mehrmals ausgeführt wird, da möglicherweise auch Umschreibungsanweisungen auf Standortebene vorhanden sind.
Wenn Sie die Rewrite-Anweisung an der Position platzieren, gelangen Sie in die Rewrite-Phase, die sich an der Position befindet In der URI-Umschreibungsphase können die Umschreibungsanweisungen auch mehrmals ausgeführt werden.
Die Umschreibungsanweisungen umfassen die Set-Anweisung und die Rewrite-Anweisung von HttpRewriteModule, die set_by_lua-Anweisung von HttpLuaModule und die set_unescape_uri-Anweisung des ngx_set_misc-Moduls Und fast alle HttpRewriteModule Die Anweisungen gehören alle zur Rewrite-Phase.
Lassen Sie uns an dieser Stelle über eine Frage nachdenken: Können diese Anweisungen an derselben Stelle koexistieren, da sie in dieser Phase neu geschrieben werden können?
Beispiel 1:
Denken Sie über die Ausgabe des folgenden Beispiels nach. Was ist das Ergebnis?
location /test { set $a 32; set $b 56; set_by_lua $c "return ngx.var.a + ngx.var.b"; set $d "$a + $b = $c"; echo $d; }
Wenn wir auf http://localhost/test zugreifen, ist die Ausgabe 32 + 56 = 88, was unseren Erwartungen entsprechen sollte.
Dies beweist jedoch nicht, dass alle Anweisungen verschiedener Module, die zu denselben Phasen gehören, in der richtigen Reihenfolge ausgeführt werden müssen, wenn sie in einer Phase koexistieren. Tatsächlich verwenden die oben genannten Module von Drittanbietern alle spezielle Techniken, um ihre eigenen Konfigurationsanweisungen in die Befehlssequenz von HttpRewriteModule zu „einzufügen“ (sie alle verwenden das von Marcus Clyne geschriebene Modul ngx_devel_kit eines Drittanbieters). Mit anderen Worten: Konventionelleres Umschreiben in Nginx Module von Drittanbietern, die die Registrierung durchführen und Anweisungen ausführen, haben nicht so viel Glück. Obwohl die Anweisungen dieser „regulären Module“ auch in der Rewrite-Phase ausgeführt werden, werden ihre Konfigurationsanweisungen und HttpRewriteModule (und andere Module in derselben Phase) unabhängig voneinander ausgeführt. Zur Laufzeit ist die Reihenfolge zwischen den Konfigurationsbefehlssätzen verschiedener Module im Allgemeinen ungewiss (streng genommen wird sie im Allgemeinen durch die Ladereihenfolge der Module bestimmt, es gibt jedoch Ausnahmen). Beispielsweise führen beide Module A und B Anweisungen in der Umschreibephase aus, sodass entweder alle Anweisungen von Modul A ausgeführt werden, bevor die Anweisungen von Modul B ausgeführt werden, oder umgekehrt, alle Anweisungen von Modul B ausgeführt werden und dann A ausgeführt wird Anweisungen ausgeführt. Sofern in der Dokumentation des Moduls nicht ausdrücklich angegeben, sollten Benutzer im Allgemeinen keine Konfigurationen schreiben, die auf dieser unbestimmten Reihenfolge basieren. Es gibt auch viele Module von Drittanbietern, ngx_array_var Und ngx_encrypted_session, das zum Ver- und Entschlüsseln von Benutzersitzungen verwendet wird, kann auch nahtlos mit den Anweisungen von HttpRewriteModule zusammenarbeiten. Das Standard-HttpRewriteModule ist so weit verbreitet, dass Module von Drittanbietern, die mit seinen Konfigurationsanweisungen gemischt werden können, von Vorteil sind. Anweisungen, die nicht mit HttpRewriteModule gemischt werden können, sollten bei der tatsächlichen Verwendung beachtet werden. Ihre Ausgabe ist undefiniert, was nichts mit ihrer Reihenfolge in der Konfigurationsdatei zu tun hat.
Fazit: Anweisungen mit Bereichen in verschiedenen Modulen derselben Phase. Wenn besondere Kompatibilität zwischen Modulen hergestellt wird, werden sie nacheinander in der Reihenfolge ausgeführt, in der die Anweisungen in der Konfigurationsdatei erscheinen
Beispiel 2:
Überlegen Sie, wie die Ausgabe unten aussieht?
location /test { set $value dog; more_set_input_headers "X-Species: $value"; set $value cat; echo "X-Species: $http_x_species"; }
说明:第三方模块 ngx_headers_more 提供了一系列配置指令,用于操纵当前请求的请求头和响应头。其中有一条名叫 more_set_input_headers 的指令可以在 rewrite 阶段改写指定的请求头(或者在请求头不存在时自动创建)。该指令的文档中有这么一行标记 phase: rewrite tail,是说这条指令总是运行在 rewrite 阶段的末尾。显然,写在
more_set_input_headers 指令之后的 set $value cat 语句却先执行了。也就是说属于HttpRewriteModule的set指令先执行完了,才执行ngx_header_more 的set_input_headers指令。
结论:即使运行在同一个请求处理阶段,分属不同模块的配置指令也可能会分开独立运行(除非像 ngx_set_misc 等模块那样针对 ngx_rewrite 模块提供特殊支持)。换句话说,在单个请求处理阶段内部,一般也会以 Nginx 模块为单位进一步地划分出内部子阶段。下面的例子3同例子2:
例子3:
location /test { set $a 1; rewrite_by_lua "ngx.var.a = ngx.var.a + 1"; set $a 56; echo $a; }
说明:HttpLuaModule的rewrite_by_lua 指令也是处在 rewrite tail phase,它也会在rewrite 阶段的末尾执行。因此HttpRewriteModule的所有set执行完后,才执行它。
显然,rewrite_by_lua 指令的行为不同于我们前面在 (二) 中介绍过的 set_by_lua 指令。
小伙伴们可能要问,既然 more_set_input_headers 和 rewrite_by_lua 指令都运行在 rewrite 阶段的末尾,那么它们之间的先后顺序又是怎样的呢?答案是:不一定。我们应当避免写出依赖它们二者间顺序的配置。
结论:作用域在同一个phase的不同modules的指令,如果没有做特殊兼容处理,则它们指令的执行顺序与指令在配置中出现的顺序无关,结果具有不确定性
location级别重写的下一阶段,用来检查上阶段是否有uri重写,并根据结果跳转到合适的阶段;
访问权限控制的前一阶段,该阶段在权限控制阶段之前,一般也用于访问控制,比如限制访问频率,链接数等;相关模块/指令 :NginxHttpLimitZoneModule / limit_zone,
HttpLimitReqModule / limit req, HttpRealIpModule
访问权限控制阶段,比如基于ip黑白名单的权限控制,基于用户名密码的认证控制等;相关模块/指令 HttpAccessModule / allow, deny, NginxHttpAuthBasicModule / auth_basic。 HttpAccessModule提供的 allow 和 deny 配置指令可用于控制哪些 IP 地址可以访问,哪些不可以。HttpAccessModule模块还支持所谓的“CIDR 记法”来表示一个网段,例如 169.200.179.4/24 则表示路由前缀是 169.200.179.0(或者说子网掩码是 255.255.255.0)的网段。
思考下下面两个例子(例子5,例子6)
例子4:
location /hello { allow 127.0.0.1; deny all; echo "hello world"; }
例子5:
location /hello { deny all; allow 127.0.0.1; echo "hello world"; }
例5和例6的区别在于deny all ,和allow 127.0.0.1 这两条指令的顺序不同。但例5中/hello 只允许从本机(IP 地址为保留的 127.0.0.1)访问,而从其他 IP 地址访问都会被拒(返回 403 错误页)。而例6中被配置为任何IP访问都会返回403错误。
原因说明:同属于HttpAccessModule这个模块的多条配置指令之间是按顺序执行的,直到遇到第一条满足条件的指令就不再执行后续的 allow 和 deny 指令。如果首先匹配的指令是 allow,则会继续执行后续其他模块的指令或者跳到后续的处理阶段;而如果首先满足的是 deny 则会立即中止当前整个请求的处理,并立即返回给客户端 403 错误页。
结论:同一个phase的同一个module内的多条指令的执行顺序由这个module自己来定义。
因为 HttpAccessModule的指令运行在 access 阶段,而 access 阶段又处于 rewrite 阶段之后,所以前面我们见到的所有那些在 rewrite 阶段运行的配置指令,都总是在 allow 和 deny 之前执行,而无论它们在配置文件中的书写顺序是怎样的。所以,为了避免阅读配置时的混乱,我们应该总是让指令的书写顺序和它们的实际执行顺序保持一致。
访问权限控制的后一阶段,该阶段根据权限控制阶段的执行结果进行相应处理;
HttpCoreModule的try_files指令的处理阶段,如果没有配置try_files指令,则该阶段被跳过; 该指令作用域: server ,location。
当try_files用于server阶段时一般是初始化作用,用来加载一些文件。
语法: try_files file1,file2,..,fileN-1 ... ,fallback 或者try_files file1,file2,..,fileN = code
用来顺序检查file1,file2,...fileN-1是否存在,如果最后一个字符为/表示这是一个目录。只要找到一个file存在,则进入到content phase,输出内容。如果前N-1个参数代表的file都不存在,此时最后一个参数发挥作用,最后一个参数用于内部跳转,并且只有最后一个参数是用作内部跳转,因此最后一个参数必须存在,否则将会引发一个内部错误。前面的file参数只是设置uri指向,不会引发内部跳转。此外注意: try_files和rewrite不同,rewrite指令会自动保存原请求的参数$args,而try_files最后的fallback参数如果需要带上请求的参数,则需要明确指出,如:
try_files $uri $uri/ /index.php?q=$uri&$args
内容生成阶段,该阶段产生响应,并发送到客户端; 这个阶段相关的模块和指令较多。这里仅拿HttpLuaModule的content_by_lua和HttpEchoModule的echo指令来举个例子再次证明上述谈论到的一个结论。
例子:
location /test { set $a 123; set $b 456; echo $b; content_by_lua ' ngx.say(ngx.var.a) '; }
location /test_echo { content_by_lua ' ngx.say(ngx.var.a) '; set $a 123; set $b 456; }
日志记录阶段,该阶段记录访问日志;
location /test { set $a 11; echo $a; set $a 22; echo $a; }
参考资料:
http://wiki.nginx.org/
http://wiki.nginx.org/Phases
http://blog.sina.com.cn/s/blog_6d579ff40100xpff.html
Das Obige ist eine Einführung in die Nginx-Phase, einschließlich der relevanten Inhalte. Ich hoffe, dass es für Freunde hilfreich ist, die sich für PHP-Tutorials interessieren.