Nginx的11个执行流程

1 Nginx简介

图片Web服务器市场份额

图片

Nginx [engine x] 最初由 Lgor Sysoev 编写。根据 Netcraft 的数据,到2020年9月,Nginx 服务或代理了25.76%站点,市场份额占到了约34.03%。

Nginx 被广泛用作:

· HTTP服务器

· 反向代理服务器

· 邮件代理服务器

· 通用的TCP/UDP代理

 

2 Nginx架构

图片

 

Nginx服务由一个Master进程和多个Worker进程构成。Master进程的主要职责是读取、解析配置文件,并维护工作进程。工作进程则负责实际的请求处理。

为了高效的在Worker之间分发请求,Nginx引入了依赖于操作系统的、高效的事件驱动模型。Worker进程的数量常常根据CPU核心数设置。这种模式的好处是每个worker进程都相互独立,无需添加锁,减少锁的开销。采用独立的进程并且不相互影响,一个进程进程退出并不影响其他的进程。一个worker进程的异常退出只会影响当前worker的上的请求,不影响其他worker的请求,降低了风险。

 

3 分Nginx处理http请求的11个阶段

Nginx 的11个执行阶段以及对应的http模块

图片

Nginx 的11个执行阶段的枚举类可以参考nginx源码中的ngx_http_core_module.h 中ngx_http_phases枚举

typedef enum {NGX_HTTP_POST_READ_PHASE = 0,NGX_HTTP_SERVER_REWRITE_PHASE,NGX_HTTP_FIND_CONFIG_PHASE,NGX_HTTP_REWRITE_PHASE,NGX_HTTP_POST_REWRITE_PHASE,NGX_HTTP_PREACCESS_PHASE,NGX_HTTP_ACCESS_PHASE,NGX_HTTP_POST_ACCESS_PHASE,NGX_HTTP_PRECONTENT_PHASE,NGX_HTTP_CONTENT_PHASE,NGX_HTTP_LOG_PHASE
} ngx_http_phases;

模块介绍可以参考Modules reference

11个执行阶段的各个模块的执行流程图:

图片

3.1 post_read 阶段介绍

Nginx的第一个阶段post_read 阶段是在正式处理请求之前工作的。在这个阶段刚刚获取了请求头的信息,还没有进行任何处理。我们可以拿到当前请求的原始数据,比如当前请求的真实IP。

在http协议中有两种方式获取用户IP:

· X-Forwardex-For 是用来传递 IP 的,这个头部会把经过的节点 IP 都记录下来。

· X-Real-IP:可以记录用户真实的 IP 地址,只能有一个。

3.1.1 ngx_http_realip_module 模块介绍

post_read涉及到的模块 ngx_http_realip_module 当前模块不会自动编译进Nginx中所以需要手动编译进入nginx源码文件夹中执行下列操作

./configure --with-http_realip_module

· 配置示例

set_real_ip_from 192.168.1.0/24;
set_real_ip_from 192.168.2.1;
set_real_ip_from 2001:0db8 :: / 32;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

· 内嵌变量

$realip_remote_addr #保留原始客户地址
$realip_remote_port #保留原始客户端端口

· 模块指令

当前模块有三个指令 set_real_ip_from、real_ip_header、real_ip_recursive

· set_real_ip_from

句法:	set_real_ip_from address | CIDR | unix:;
默认:	-
内容:	http,server,location

指定可信的地址,只有从该地址建立的连接,获取的 realip 才是可信的

· real_ip_header

句法:	real_ip_header field | X-Real-IP | X-Forwarded-For | proxy_protocol;
默认:	real_ip_header X-Real-IP;
内容:	http,server,location

指定从哪个头部取真实的 IP 地址,默认从 X-Real-IP 中取,如果设置从 X-Forwarded-For 中取,会先从最后一个 IP 开始取

· real_ip_recursive

句法:	real_ip_recursive on | off;
默认:	real_ip_recursive off;
内容:	http,server,location

环回地址,默认关闭,打开的时候,如果 X-Forwarded-For 最后一个地址与客户端地址相同,会过滤掉该地址

· 实战 realIp 模块

修改nginx.conf 配置文件内容如下:

server {listen       80;server_name  abbila.com;set_real_ip_from 10.211.55.2;real_ip_recursive  off;#real_ip_recursive  on;real_ip_header  X-Forwarded-For;location / {return 200 "client real Ip is:$remote_addr  \n";}
」

上面的配置中设置了可信Ip为10.211.55.2,real_ip_recursive是关闭状态的,real_ip_header 从X-Forwarded-For获取。测试结果:

curl -H "X-Forwarded-For: 1.1.1.1,22.22.22.22,10.211.55.2" abbila.com
client real Ip is:10.211.55.2

然后把real_ip_recursive 打开 内容如下:

server {listen       80;server_name  abbila.com;set_real_ip_from 10.211.55.2;#real_ip_recursive  off;real_ip_recursive  on;real_ip_header  X-Forwarded-For;location / {return 200 "client real Ip is:$remote_addr  \n";}
」

测试结果

curl -H "X-Forwarded-For: 1.1.1.1,22.22.22.22,10.211.55.2" abbila.com
client real Ip is:22.22.22.22

通过上面两个实验可以看出来,如果real_ip_recursive关闭的话,获取的realIp为X-Forwarded-For的最后一个IP如果是打开状态的话那就把X-Forwarded-For中与可信IP重复的IP过滤掉然后取剩下的最后一个IP为realip。如果使用 X-Forwarded-For 获取 realip 的话,real_ip_recursive 需要打开。并且,realip 依赖于 set_real_ip_from 设置的可信地址。那么有人可能就会问了,那直接用 X-Real-IP 来选取真实的 IP 地址不就好了。这是可以的,但是 X-Real-IP 是 Nginx 独有的,如果客户端与服务器之间还有其他非 Nginx 软件实现的代理,就会造成取不到 X-Real-IP 头部,所以这个要根据实际情况来定。

3.2 rewrite 阶段

3.2.1 ngx_http_rewrite_model 模块介绍

rewrite 阶段设计到两个部分

· NGX_HTTP_REWRITE_PHASE

· NGX_HTTP_SERVER_REWRITE_PHASE

当前模块有 break、if、return、rewrite、rewrite_log、set、uninitialized_variable_warn 指令

· break 指令

句法:	break;
默认:	—
内容:	server,location,if

停止处理当前ngx_http_rewrite_module指令集 。

· if 指令

句法:	if (condition) { ... }
默认:	—
内容:	server, location

如果condition为true则执行{}中的内容。
condition可以为以下的一种或者几种:

· 变量名;如果变量的值为空字符串或“ 0”,则为false;否则为false。

· 使用“ =”和“ !=”运算符将变量与字符串进行比较。

· 使用“ ~”(区分大小写的匹配)和“ ~*”(区分大小写的匹配)运算符将变量与正则表达式进行匹配。正则表达式可以包含捕获。

这些捕获可用于以后在$1…$9变量中重用。负运算符“ !~”和“ !~*”也可用。如果正则表达式包含“ }”或“ ;”字符,则整个表达式应用单引号或双引号引起来。

· 使用“ -f”和“ !-f”运算符检查文件是否存在;

· 使用“ -d”和“ !-d”运算符检查目录是否存在;

· 使用“ -e”和“ !-e”运算符检查文件,目录或符号链接是否存在;

· 使用“ -x”和“ !-x”运算符检查可执行文件。

· return 指令

句法:	return code [text];return code URL;return URL;
默认:	—
内容:	server,location,if

状态码可以包含以下几种:

· Nginx 自定义

· 444:立刻关闭连接,用户收不到响应

· HTTP 1.0 标准:

· 301:永久重定向

· 302:临时重定向,禁止被缓存

· HTTP 1.1 标准:

· 303:临时重定向,允许改变方法,禁止被缓存

· 307:临时重定向,不允许改变方法,禁止被缓存

· 308:永久重定向,不允许改变方法

也可以

return 200 "the status code is 200";

可以返回对应的 error_page 可以参考:

http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page

· rewrite 指令

句法:	rewrite regex replacement [flag];
默认:	—
内容:	server,location,if

可选flag参数可以是以下之一:

· last
停止处理当前ngx_http_rewrite_module指令集, 并开始搜索与更改后的URI相匹配的新位置;

· break
ngx_http_rewrite_module与break指令一样, 停止处理当前的指令集 ;

· redirect
返回带有302代码的临时重定向;如果替换字符串不是以“ http://”,“ https://”或“ $scheme” 开头,则使用

· permanent
返回带有301代码的永久重定向。

· rewrite_log 指令

句法:	rewrite_log on | off;
默认:	rewrite_log off;
内容:	http,server,location,if

如果打开当前指令之后,会把 rewrite 的日志写入 logs/rewrite_error.log 日志文件中,

· set 指令

句法:	set $variable value;
默认:	—
内容:	server,location,if

可以为variable 设置对应的value值。该value可以包含文本,变量,他们的组合。

· uninitialized_variable_warn 指令

句法:	uninitialized_variable_warn on | off;
默认:	uninitialized_variable_warn on;
内容:	http,server,location,if

控制是否记录有关未初始化变量的警告。比如有些值没有找到等等。

· 实战一下rewrite模块:

nginx.conf 配置文件如下:

server {listen       80;server_name  abbila.com;location /break/ {rewrite ^/break/(.*) /test/$1 break;}location /last/ {rewrite_log on;rewrite ^/last/(.*) /test/$1 last;}location /test/ {return 200 "test page";}
}

测试下last:

curl abbila.com/last/ 
test page%

在测试下break:

curl abbila.com/break/  <html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>

通过测试可以看到break是跳过当前请求的rewrite阶段,并继续执行本请求的其他阶段,last与break最大的不同是,last会重新发起一个新请求,并重新匹配location,所以对于/last,重新匹配请求以后会匹配到/test/,所以最终对应的content阶段的输出是test page;然后看一下日志error.log

2020/10/16 15:51:25 [error] 29625#0: *26 "/usr/local/webserver/nginx/html/test/index.html" is not found (2: No such file or directory), client: 10.211.55.2, server: abbila.com, request: "GET /break/ HTTP/1.1", host: "abbila.com"
2020/10/16 15:51:36 [notice] 29625#0: *27 "^/last/(.*)" matches "/last/", client: 10.211.55.2, server: abbila.com, request: "GET /last/ HTTP/1.1", host: "abbila.com"
2020/10/16 15:51:36 [notice] 29625#0: *27 rewritten data: "/test/", args: "", client: 10.211.55.2, server: abbila.com, request: "GET /last/ HTTP/1.1", host: "abbila.com"

因为在break里面没有打开rewrite_log 在last中设置了rewrite_log 可以看到里面有rewrite日志信息,注意要把error.log的级别调成notice级别。

error_log  logs/error.log  notice;

3.3 preaccess 阶段

我们经常会遇到一个问题,就是如何限制每个客户端的并发连接数?如何限制访问频率?当前阶段设计到两个模块 ngx_http_limit_conn_module ngx_http_limit_req_module这两个模块都是默认编译到nginx中的可以使用–without-http_limit_req_module --without-http_limit_conn_module 移除。

3.3.1 http_limit_req_module 和 http_limit_conn_module 模块

req模块有四个指令 limit_req、limit_req_log_level、limit_req_status、limit_req_zone

与指对应的conn也存在四个指令limit_conn、limit_conn_log_level、limit_conn_status、limit_conn_zone

· limit_req 指令

句法:	limit_req zone=name [burst=number] [nodelay | delay=number];
默认:	—
内容:	http, server, location

定义共享内存(包括大小),以及 key 关键字和限制速率 rate 单位为 r/s 或者 r/m(每分钟或者每秒处理多少个请求)

· limit_req_log_level指令

句法:	limit_req_log_level info | notice | warn | error;
默认:	limit_req_log_level error;
内容:	http,server,location

如果超过了设置的阀值则把日记的记录级别修改为对应的级别。

· limit_req_status 指令

句法:	limit_req_status code;
默认:	limit_req_status 503;
内容:	http,server,location

为拒绝的服务请求设置对应点状态码。

· limit_req_zone 指令

句法:	limit_req_zone key zone=name:size rate=rate [sync];
默认:	—
内容:	http

设置共享内存区域的参数,该参数将保留各种键的状态。特别是,状态存储当前的过多请求数。该key可以包含文本,变量,他们的组合。具有空键值的请求不予考虑。

可以看到上面各个模块的执行顺序。当两个同事设置时,limit_req 在 limit_conn 处理之前,因此是 limit_req 会生效
如果不添加 nodelay,请求会等待,直到能够处理请求;添加 nodelay,在不超出 burst 的限制的情况下会立刻处理并返回,超出限制则会返回 limit_req_status状态。

· 实战:限流模块

nginx.conf 配置如下

limit_conn_zone $binary_remote_addr zone=addr:10m; #申请共享内存
server {listen       80;server_name  abbila.com;location / {limit_conn_status 500;limit_conn_log_level  warn;limit_rate 50;limit_conn addr 1;#limit_req zone=one burst=3 nodelay;#limit_req zone=one;    }
}

通过curl 访问

curl abbila.com/last/
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>

因为设置的limit_rate 是50个字节每秒,所以返回会特别慢,限制发生时设置的status是500所以同时发送两个请求会返回

curl abbila.com/last/
<html>
<head><title>500 Internal Server Error</title></head>
<body>
<center><h1>500 Internal Server Error</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>

3.4. access 阶段

当前模块涉及到ngx_http_access_module模块、ngx_http_auth_basic_module 、ngx_http_auth_request_module

3.4.1 ngx_http_access_module 模块

当前模块默认编译到nginx中,可以使用–without-http_access_module

当前模块有两个指令allow、deny

· allow 指令

句法:	allow address | CIDR | unix: | all;
默认:	—
内容:	http,server,location,limit_except

允许访问指定的网络或地址。如果unix:指定了特殊值,则允许访问所有UNIX域套接字。

· deny 指令

句法:	deny address | CIDR | unix: | all;
默认:	—
内容:	http,server,location,limit_except

拒绝访问指定的网络或地址。如果unix:指定了特殊值,则允许访问所有UNIX域套接字。

3.4.2 ngx_http_auth_basic_module 模块

当前模块默认编译进nginx中,可使用 --without-http_auth_basic_module 指令移除
该模块允许通过使用“ HTTP基本身份验证”协议验证用户名和密码来限制对资源的访问。当开启这这个模块后,如果通过浏览器访问url时
会返回"401 Unauthorized" ,浏览器会返回一个用户名密码的对话框。
该模块有两个指令auth_basic、auth_basic_user_file

· auth_basic 指令

句法:	auth_basic string | off;
默认:	auth_basic off;
内容:	http,server,location,limit_except

· auth_basic_user_file

句法:	auth_basic_user_file file;
默认:	—
内容:	http,server,location,limit_except

这里面会用到 htpasswd(依赖httpd-tools安装包,如果自己试验可自行安装),这个工具可以用来生成密码文件,而 auth_basic_user_file 就依赖这个密码文件。
使用方法:

htpasswd -c file -b user password

· 实战:鉴权模块

nginx.conf 配置文件内容,通过上面命令生成 abbila.pass 文件。

server {listen       80;server_name  abbila.com;location / {auth_basic "close the web site";auth_basic_user_file abbila.pass;}
|

然后通过浏览器访问nginx 地址就可以看到:

图片

然后输入账号密码就可以正常访问页面了。

图片

3.4.3 ngx_http_auth_request_module 模块

当前模块没有编译到nginx中,可使用 --with-http_auth_basic_module 添加到nginx中。该模块可以将客户端输入的用户名、密码 username:password 通过 Base64 编码后写入 Request Headers 中。例如:abbila:abbila -> Authorization: Basic YWJiaWxhMTphYmJpbGE= 然后通过第三方程序解码后跟数据库中用户名、密码进行比较,Nginx 服务器通过 header 的返回状态判断是否认证通过。当前模块设计两个指令auth_request、auth_request_set

· auth_request 指令

句法:	auth_request uri | off;
默认:	auth_request off;
内容:	http,server,location

如果子请求返回2xx响应代码,则允许访问。如果返回401或403,则使用相应的错误代码拒绝访问。子请求返回的其他响应代码都被视为错误。

· auth_request_set 指令

句法:	auth_request_set $variable value;
默认:	—
内容:	http,server,location

授权成功后可以把赋值一些变量,可以包括鉴权请求的变量。

配置示例:

location /private/ {auth_request /auth;...
}location = /auth {proxy_pass https://auth.server.com/HttpBasicAuthenticate;#认证服务地址proxy_pass_request_body off;proxy_set_header Content-Length "";proxy_set_header X-Original-URI $request_uri;
}

根据认证服务地址返回的状态码来判断是否可以访问 /private/ 的请求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/535405.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nginx的执行阶段详解

在了解nginx的执行阶段前&#xff0c;先看一个例子 对echo不熟悉的&#xff0c;可以先看文章Nginx调试必备了解下echo扩展 回到上面这个例子&#xff0c;在server块中配置这样的location&#xff0c;你觉得输出是什么样子&#xff1f; 按照正常的逻辑&#xff0c;输出应该是32 …

Docker挂了,数据如何找回

docker在实际使用中&#xff0c;让运维人员诟病的&#xff0c;除了安全问题外&#xff0c;大概就是数据的问题了 很多人在初用docker的时候&#xff0c;很多时候都忘记或不知道docker中需要保留的数据需要挂载到宿主机文件夹到容器内部对应目录&#xff08;当然除了挂载宿主机目…

TCP总结

TCP这些东西&#xff0c;基本每个程序猿都或多或少是掌握的了。虽然感觉在实际开发中没有什么用武之处&#xff0c;但&#xff0c;面试他要问啊 而最近大家伙过完年&#xff0c;也都在准备春招&#xff0c;我也一样。阅读了一些okHttp源码之后&#xff0c;又屁颠屁颠地跑回来重…

LDAP组的概念以及命令

Oracle统一目录支持组&#xff0c;组是作为单个对象管理的条目集合。通常&#xff0c;目录管理员配置打印机组、软件应用程序组、员工组等。在为一组用户分配特殊访问权限时&#xff0c;组尤其有用。例如&#xff0c;您可以配置一组访问管理器&#xff0c;并分配权限&#xff0…

oracle中修改process

可以用如下命令查看数据库连接的消耗情况 select b.MACHINE, b.PROGRAM, b.USERNAME, count(*) from v$process a, v$session b where a.ADDR b.PADDR and b.USERNAME is not null group by b.MACHINE, b.PROGRAM, b.USERNAME order by count(*) desc 在 oracle中&…

安装python3.9

GCC版本 这个版本的编译器不适合编译Python3.9&#xff0c;在编译时会产生如下的错误。我们用这个老版本编译器编译一个新的GCC 9.2版。 Could not import runpy module Traceback (most recent call last):File "Python-3.8.1/Lib/runpy.py", line 15, in <mod…

Oracle数据库游标数总结

各用户的打开游标总数 SELECT A.USER_NAME, COUNT(*) FROM V$OPEN_CURSOR A GROUP BY A.USER_NAME; 查找数据库各用户各个终端的缓存游标数 SELECT AA.USERNAME, AA.MACHINE, SUM(AA.VALUE) FROM ( SELECT A.VALUE, S.MACHINE, S.USERNAME FROM V$SESSTAT A, V$STATNAME B, V…

logstash安装

下载最新版logstash https://www.elastic.co/cn/downloads/logstash 解压缩 tar zxvf logstash-7.12.1-linux-x86_64.tar.gz 下载jdk1.8 tar zxvf jdk-8u291-linux-x64.tar.gz 编辑启动文件logstash、logstash.lib.sh、logstash-plugin 在首行添加 export JAVA_C…

[logstash-input-file]插件使用详解

这个插件可以从指定的目录或者文件读取内容&#xff0c;输入到管道处理&#xff0c;也算是logstash的核心插件了&#xff0c;大多数的使用场景都会用到这个插件&#xff0c;因此这里详细讲述下各个参数的含义与使用 1 path 是必须的选项&#xff0c;每一个file配置&#xff0c…

[logstash-input-log4j]插件使用

Log4j插件可以通过log4j.jar获取Java日志&#xff0c;搭配Log4j的SocketAppender和SocketHubAppender使用&#xff0c;常用于简单的集群日志汇总。 最小化的配置 input {log4j {host>"localhost"port>4560} } output {stdout {} } log4j插件配置host以及port就…

logstash-input-redis插件使用详解

input {#redis {#host> "10.246.187.12"#redis地址#host> "10.246.152.116"#redis地址#port > "6379" #redis端口号#password > "123qwe" #如果有安全认证&#xff0c;此项为密码#key > "logstash:redis"#ty…

logstash-input-redis源码解析

首先是程序的自定义&#xff0c;这里设置了redis插件需要的参数&#xff0c;默认值&#xff0c;以及校验等。 然后注册Redis实例需要的信息&#xff0c;比如key的名字或者url等&#xff0c;可以看到默认的data_type是list模式。 程序运行的主要入口&#xff0c;根据不同的dat…

SSL双向认证和SSL单向认证的区别

双向认证 SSL 协议要求服务器和用户双方都有证书。单向认证 SSL 协议不需要客户拥有CA证书&#xff0c;具体的过程相对于上面的步骤&#xff0c;只需将服务器端验证客户证书的过程去掉&#xff0c;以及在协商对称密码方案&#xff0c;对称通话密钥时&#xff0c;服务器发送给客…

双向认证SSL原理

文中首先解释了加密解密的一些基础知识和概念&#xff0c;然后通过一个加密通信过程的例子说明了加密算法的作用&#xff0c;以及数字证书的出现所起的作用。接着对数字证书做一个详细的解释&#xff0c;并讨论一下windows中数字证书的管理&#xff0c;最后演示使用makecert生成…

Xtrabackup备份与恢复

一、Xtrabackup介绍 Percona-xtrabackup是 Percona公司开发的一个用于MySQL数据库物理热备的备份工具&#xff0c;支持MySQL、Percona server和MariaDB&#xff0c;开源免费&#xff0c;是目前较为受欢迎的主流备份工具。xtrabackup只能备份innoDB和xtraDB两种数据引擎的表&…

实时备份工具之inotify+rsync

1.inotify简介 inotify 是一个从 2.6.13 内核开始&#xff0c;对 Linux 文件系统进行高效率、细粒度、异步地监控机制&#xff0c; 用于通知用户空间程序的文件系统变化。可利用它对用户空间进行安全、性能、以及其他方面的监控。Inotify 反应灵敏&#xff0c;用法非常简单&…

mysql主从延迟

在实际的生产环境中&#xff0c;由单台MySQL作为独立的数据库是完全不能满足实际需求的&#xff0c;无论是在安全性&#xff0c;高可用性以及高并发等各个方面 因此&#xff0c;一般来说都是通过集群主从复制&#xff08;Master-Slave&#xff09;的方式来同步数据&#xff0c…

16张图带你吃透高性能 Redis 集群

现如今 Redis 变得越来越流行&#xff0c;几乎在很多项目中都要被用到&#xff0c;不知道你在使用 Redis 时&#xff0c;有没有思考过&#xff0c;Redis 到底是如何稳定、高性能地提供服务的&#xff1f; 你也可以尝试回答一下以下这些问题&#xff1a; 我使用 Redis 的场景很…

Redis与MySQL双写一致性如何保证

谈谈一致性 一致性就是数据保持一致&#xff0c;在分布式系统中&#xff0c;可以理解为多个节点中数据的值是一致的。 强一致性&#xff1a;这种一致性级别是最符合用户直觉的&#xff0c;它要求系统写入什么&#xff0c;读出来的也会是什么&#xff0c;用户体验好&#xff0c;…

weblogic忘记console密码

进入 cd /sotware/oracle_ldap/Middleware/user_projects/domains/base_domain/security/ 目录 执行 java -classpath /sotware/oracle_ldap/Middleware/wlserver_10.3/server/lib/weblogic.jar weblogic.security.utils.AdminAccount weblogic(账号) weblogic123(密码) . …