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/ 的请求。

Nginx的11个执行流程相关推荐

  1. nginx学习笔记七(nginx HTTP框架的执行流程)

    之前已经介绍过nginx的事件框架.那么,对于client发出的一个http的请求,nginx的http框架是如何一步步解析这个http请求?http框架又是如何和之前介绍过得epoll事件模块结合起 ...

  2. Nginx中浏览器缓存的执行流程

    浏览器缓存的执行流程 HTTP协议中和页面缓存相关的字段,我们先来认识下: header 说明 Expires 缓存过期的日期和时间 Cache-Control 设置和缓存相关的配置信息 Last-M ...

  3. mysql8.0源代码解析_MySQL8.0.11源码分析之mysql关键函数和执行流程

    mysql是命令行客户端程序 ,交互式输入SQL语句或从文件以批处理模式执行它们的命令行工具. 入口函数 int main(int argc, char *argv[]) { if (get_opti ...

  4. openresty开发系列31--openresty执行流程

    openresty开发系列31--openresty执行流程 我们先看个例子 location /test {     set $a 32;     echo $a;     set $a 56;   ...

  5. 动态执行流程分析和性能瓶颈分析的利器——gperftools的Cpu Profiler

    在<动态执行流程分析和性能瓶颈分析的利器--valgrind的callgrind>中,我们领略了valgrind对流程和性能瓶颈分析的强大能力.本文将介绍拥有相似能力的gperftools ...

  6. 动态执行流程分析和性能瓶颈分析的利器——valgrind的callgrind

    在<内存.性能问题分析的利器--valgrind>一文中我们简单介绍了下valgrind工具集,本文将使用callgrind工具进行动态执行流程分析和性能瓶颈分析.(转载请指明出于brea ...

  7. 使用Caffe进行手写数字识别执行流程解析

    之前在 http://blog.csdn.net/fengbingchun/article/details/50987185 中仿照Caffe中的examples实现对手写数字进行识别,这里详细介绍下 ...

  8. Java多线程- 线程池的基本使用和执行流程分析 - ThreadPoolExecutor

    线程池的实现原理 池化技术 一说到线程池自然就会想到池化技术. 其实所谓池化技术,就是把一些能够复用的东西放到池中,避免重复创建.销毁的开销,从而极大提高性能. 常见池化技术的例如: 线程池 内存池 ...

  9. 追源索骥:透过源码看懂Flink核心框架的执行流程

    https://www.cnblogs.com/bethunebtj/p/9168274.html 追源索骥:透过源码看懂Flink核心框架的执行流程 前言 1.从 Hello,World WordC ...

最新文章

  1. P3809【模板】后缀排序
  2. spring boot实现软删除
  3. android activity启动流程_Activity 启动流程(二)
  4. fhq_treap || BZOJ 3223: Tyvj 1729 文艺平衡树 || Luogu P3391 【模板】文艺平衡树(Splay)...
  5. 患者如何区分股癣和银屑病+药膏根治
  6. 静物摄影用光技巧_详解摄影用光技巧,用好光线,拍出好照片。
  7. 【练习】使用事务控制语句
  8. Spring: 读取 .properties 文件地址,json转java对象,el使用java类方法相关 (十三)
  9. 基于Spring + Spring MVC + Mybatis 高性能web构建
  10. 高项论文(沟通管理)
  11. hdu4282 A very hard mathematic problem
  12. win10u盘被写保护怎么解除_win10系统磁盘被写保护如何解除 磁盘被写保护解除方法...
  13. ROS之launch文件解析
  14. CF1144C - Two Shuffled Sequences
  15. 实战攻防之紫队视角下的实战攻防演习组织
  16. 关于Mongodb的全面总结
  17. c++狼人杀12人标准场有发言有选警附exe文件
  18. 为什么学完这个他们都跳槽/涨薪了?
  19. 视频号直播刚改版后怎么开通企业微信
  20. 万姓女孩清秀文雅的名字

热门文章

  1. r语言查找是否存在空值_关于R包安装你知道多少?
  2. full join 和full outer join_多表关联:公式展开、join、过滤条件的顺序
  3. IOS – OpenGL ES 调节图像对比度 GPUImageContrastFilter
  4. Pycharm 提示:this license * has been cancelled - Python零基础入门教程
  5. java基数排序 数组_万字长文带你掌握Java数组与排序,代码实现原理都帮你搞明白!...
  6. 防火墙设置导致服务器站点打开,服务器、网站、环境配置全正常网站打不开原来是系统防火墙造成的...
  7. android加一减一控件,Android的步进器(增加/减少值)控件?
  8. python网络库_python的网络库
  9. 大学计算机课第二章内容总结,第四周市政系《大学计算机基础》课程总结
  10. python3类的继承详解_基于python3 类的属性、方法、封装、继承详解