Nginx系列之请求处理的11个阶段(上)

本文深入探讨了Nginx处理请求的11个阶段,重点讲解了realip模块在获取真实客户端IP、rewrite模块的return和error_page指令以及Location指令的匹配规则。通过实例解析了realip模块如何在反向代理场景下获取客户端IP,rewrite模块的return和error_page如何影响请求流程,以及Location匹配的详细逻辑。
摘要由CSDN通过智能技术生成

http模块是Nginx的重要模块,此篇博客将围绕Nginx处理请求时的11个阶段展开,并通过实际例子来演示不同阶段引入的module的作用。下图是不同阶段涉及的module说明。下面将依次介绍实际的模块作用。

 下图是处理请求的11个阶段中,不同模块执行顺序,同一阶段中如果有多个模块,会按下图顺序执行不同模块。另外,还可以在编译后的objs目录下查看ngx_module.c文件,里面存放的模块顺序与模块的执行顺序刚好相反。例如limit_req先于limit_conn执行,在ngx_module.c文件中,从上往下看,先是limit_conn,然后是limit_req.大部分场景下会按如下的模块顺序执行,但也有例外,比如access模块的satisfy指令,当设置satisfy any时,access模块执行后,不再执行auth_basic和auth_request模块,而是跳到try_files模块执行指令。下图中灰色的三个模块(rewrite,find_config,rewrite) 属于nginx框架执行部分,第三方模块无法在这里进行自定义的实现。

接下来就按照模块执行顺序通过实际例子介绍每个模块的主要作用。

 realip模块:当本机的nginx处于一个反向代理的下游时,获取真实的客户端IP地址

如上图所示,假设用户访问Nginx服务,先经过家里的路由器分配的内部IP地址,再经过运营商的ADSL,也就是真实的一个公网地址,到达CDN,再经过某个反向代理服务,最终才访问到Nginx。在建立连接时,HTTP的头部会有X-Forward-For字段和X-Real-IP字段,这两个字端中会存放经历过的发送请求的服务的IP地址信息和真实的客户端IP地址信息。例如经过CDN往下发送请求时,CDN在请求中就会添加X-Forward-For字段和X-Real-IP字段,达到某反向代理服务器时会再次修改这两个字端的值,需要注意的是:X-Real-IP只能有一个值,而X-Forward-For值是累加的过程。当到达Nginx服务时,如果在没有引入realip模块的情况下,获取$remote_addr,读取到的信息是上一个发送请求的服务地址,也就是2.2.2.2,而实际真正想获取的客户的公网IP是115.204.33.1,获取到IP后就可以进行限流等配置。在这种情况下如何获取真实的客户端公网IP呢?答案是引入realip模块,默认情况下编译nginx不会有此模块,需要通过--with-xx命令添加该模块。引入realip模块后,$remote_addr字段的值会被写成在传递过程中的X-Forward-For的值,而原来的$remote_addr的值,如果要获取,可以通过$realip_remote_addr获取。下面是一段关于realip的配置,可以通过include命令引入到nginx.conf文件中。

server {
    server_name localhost;
	error_log logs/myerror.log debug;
	set_real_ip_from  10.206.21.246;
	#real_ip_header X-Real-IP;
	real_ip_recursive off;
	#real_ip_recursive on;
	real_ip_header    X-Forwarded-For;

	location /{
		return 200 "Client real ip: $remote_addr, $realip_remote_addr, $binary_remote_addr\n";
	}

}

当上述配置生效后,如果执行命令:“curl -H 'X-Forwarded-For: 1.1.1.1,10.206.21.246' localhost",此时打印出来的$remote_addr值是10.206.21.246,如果开启real_ip_recursive,那么打印出来的值是1.1.1.1。也就是说,当关闭real_ip_recursive时,服务会取X-Forwarded-For中的最后一个IP地址作为客户端真实地址,当开启real_ip_recursive时,服务会取X-Forwarded-For中的值,如果IP地址等于设置的set_real_ip_from值,会跳过,直到找到一个不等于设置的real_ip_from的值时,将此IP地址作为客户端地址。可以这样理解,当客户端发送请求时,会经过中间哪些服务才达到Nginx,团队是知道的,需要把这些中间服务的IP设置为real_ip_from,这样当客户端发送请求时,就能获取到真正的客户端IP信息。上面还提到header中会通过X-Real-IP传递真实客户端IP的信息,为什么不直接读取X-Real-IP的值呢?因为X-Real-IP是Nginx独有的,不是RFC规范,所以与client间交互时,如果还有其他非Nginx软件实现的代理,将取不到X-Real-IP头部。

rewrite模块下的return和error_page指令

在处理请求的11个阶段中,当return指令生效后,后续的指令都不会再生效。左边是return不同状态码的含义说明,右边是error_page返回不同的状态码,及跳转的页面或者地址配置方式。

 

下面通过实际例子来演示开启不同配置的区别,结果如下图所示:假设注释掉location中的return和server中的return指令,访问“curl http://taoli.test.pub:8070/test.txt”,因为配置了root html/,所以当curl的时候,会从nginx的html文件夹下查找是否存在test.txt文件,实际目录中不存在该文件,所以会执行error_page 404的结果。此时,将location中的return注释取消掉,再次访问,会返回“find nothing”的信息,说明同时配置error_page和return指令时,会优先执行return指令。此时再把server中的return指令的注释也取消掉,再次访问服务,会返回405的信息,因为server和location中的return指令都属于rewrite阶段,server模块的指令会先于location中的指令执行。故返回405.

另外,需要注意,error_page最后接的是uri地址而不是文件路径。假如设置了server_name是 taoli.test;   root /var/www;  error_page 404 /404.html; 当出现404时会跳转到 http://taoli.test/404.html。如果写成 error_page 404 404.html,当访问 http://taoli.test/sss/ddd 出现404时,他会跳到 http://taoli.test/sss/ddd/404.html。

rewrite模块下的rewrite指令

rewrite后面配置期望替换的url,替换的url中可以使用正则表达式,变量等。替换后的url后面还可以配置不同的flag来进行不同的处理,flag和处理逻辑规则如下
--last 用replacement这个URL进行新的location匹配
--break break指令停止当前脚本指令的执行,等价于独立的break指令
--redirect 返回302重定向
--permanent 返回301重定向

下面通过实际例子来演示不同rewrite后,实际访问到的页面。假设在nginx的html目录下有三个目录first,second,third,三个目录下分别存放了txt文件,location中配置如下所示

当注释掉break的那一条指令时,执行“curl http://taoli.test.pub:8060/first/3.txt”返回的是 “this is 3.txt”,因为当访问/first时,被重定向到了/second,而/second下面又重定向到了/third下面,所以最终返回3.txt。当访问"curl http://taoli.test.pub:8060/second/3.txt" ,同理。当取消掉break这条指令的注释后,访问“curl http://taoli.test.pub:8060/first/3.txt”返回了"second",因为当/first重定向到/second时有break,取消了当前的重定向规则,所以执行了/second里面的return指令。

rewrite模块下的If指令

 如上图所示,当写入if指令时,if中的判断条件可以有变量,正则表达式,等于符号等。这里总结了常用的一些if表达式,如下所示

  • 检查变量为空或者值是否为0,直接使用
  • 将变量与字符串做匹配,使用=或者!=
  • 将变量与正则表达式做匹配,如果需要大小写敏感,使用~或者!~,大小写不敏感,使用~*或者!~*。
  • 检查文件是否存在,使用-f或者!-f
  • 检查目录是否存在,使用-d或者!-d
  • 检查文件、目录、软链接是否存在,使用-e或者!-e
  • 检查是否为可执行文件,使用-x或者!-x

Find_config阶段的Location指令块

Location指令块在进行匹配时,主要分为前缀匹配和正则表达式匹配,在进行这两类匹配时遵循下面的规则。

前缀匹配
=:精确匹配
^~:进行前缀匹配时,如果字符串匹配上,则不再进行正则表达式匹配
正则表达式匹配
~:大小写敏感的正则匹配
~*:忽略大小写的正则匹配

当同时多种规则符合时,最终会采用哪一条规则呢?实际Nginx在进行匹配时执行顺序如下所示:

首先会遍历所有的Location下的前缀匹配,如果命中精确匹配,那么采用精确匹配的location,如果未命中精确匹配,命中了^~后的字符串(即访问路径的字符串值等于^~后的字符串),则采用^~的location。如果都未命中,则进行正则表达式匹配,命中,则采用正则表达式的location,如果有多个正则表达式,按在location中的配置顺序,排在前面的生效。如果都没有命中,则记录最长匹配的前缀字符串locaiton,最长匹配指location中的字符串能匹配到的长度最长的那一条。接下来通过实际例子来看看。

假设curl命令访问左边的地址,左边是配置文件中的location指令块的配置,执行每一条命令后,最终会返回什么结果呢?执行结果如下图所示。这里来分析下为什么会得到下面的实验结果,从一条命令开始。

当访问/Test1时,精确匹配到了上图右边的最后一条规则,所以返回"exact match"

当访问/Test1/时,实际匹配了上图右边的第一、二、三、五条规则,匹配到了第三条的字符串,且第三条规则是^~开头,所以采用第三条规则的location,返回"stop regular expressions match"

当访问/Test1/Test2时,匹配到了第一、二、四、五条规则,因为第二条属于正则表达式,所以采用了第二条的locaiton,返回"longest regular expressions match"

当访问/Test1/Test2/时,匹配到了第四、五条规则,第四条规则的前缀更长,所以返回"longest prefix string match"

以上就是对Location匹配的介绍,下一篇博客中会接着继续介绍处理请求的11个阶段所涉及的模块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

taoli-qiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值