Nginx 执行流程
Nginx 启动
Nginx 启动、退出时的回调方法
init_module 在 master 进程中调用
init_process 在 worker 进程中调用
exit_process 在 worker 进程退出时调用
exit_master 在 master 进程退出时调用
Nginx 的启动流程
根据命令行得到配置文件路径
如果处于升级中则监听环境变量里传递的监听句柄
调用所有核心模块的 create_conf 方法生成存放配置项的描述体
针对所有核心模块解析 nginx.conf 配置文件
调用所有核心模块的 init_conf 方法
创建目录、打开文件、初始化共享内存等进程间通信方式
打开由各 Nginx 模块从配置文件中读取到的监听端口
调用所有模块的 init_module 方法(检测 Nginx 的运行方式,单进程还是 master 多进程)
一般会以 master 多进程方式运行 Nginx,进入 master 模式
启动 master 进程
根据 worker_process 启动 worker 进程,调用所有模块的 init_process 方法
启动 cache manager 进程
启动 cache loader 子进程,关闭父进程启动时监听的端口
HTTP 请求处理的 11 个阶段
HTTP 请求处理的阶段为:POST_READ、SERVER_REWRITE、FIND_CONFIG、REWRITE、POST_REWRITE、PREACCESS、ACCESS、POST_ACCESS、PRECONTENT、CONTENT、LOG
1、postread 阶段
这个阶段中,获取真实客户端地址的 realip 模块会生效。
realip 模块
默认不会编译进 Nginx,需要通过 --with-http_realip_module
启用功能,作用是修改客户端的地址。
因为模块覆盖了 $remote_addr
,为了能得到原来的 ip 信息,模块创建了两个变量:
realip_remote_addr
realip_remote_port
模块有三个指令:
set_real_ip_from:决定什么 ip 地址才是可信的,能替换
$remote_addr
变量real_ip_header:从哪个头字段中获取替换的 ip
real_ip_recursive:环回地址,如果 X-Forwarded-For 中的最后一个 ip 是本地 ip,就获取上一个 ip。
网络中存在许多反向代理的情况下,如何拿到真实的用户 IP 地址?
例如用户的地址是:122.3.3.2,经过许多反向代理后,$remote_addr
可能就变成了 8.8.8.8
HTTP 头部 X-Forwarded-For 用于传递 IP,它可以设置多个 IP,用逗号分割。
HTTP 头部 X-Real-IP 用于传递用户 IP,它只能设置一个 IP
根据 relip 模块的指令,relip 模块可以通过这些 HTTP 头部字段覆写入 binary_remote_addr、remote_addr 变量,使变量的值成为真实的用户 IP。并且只有拿到真实用户的 IP 后做连接限制(如 preaccess 阶段生效的 limit_conn 模块)才有意义。
2、server_rewrite 和 rewrite 阶段
server_rewrite 和 rewrite 这两个阶段中,rewrite 模块都会生效。
return 指令
return 指令生效后,后面的指令都不会再执行。
return 指令后面可以是 code [text]
、code URL
、URL
。要特别注意一下使用上下文,没有 http,只能用在 server,location,if
上下文中。
code 返回状态码
Nginx 自定义
444 关闭连接,此状态码不会返回到客户端
HTTP 1.0 标准
301 http1.0 永久重定向,客户端再次访问时会直接访问新地址
302 临时重定向,禁止被缓存
HTTP 1.1 标准
303 临时重定向,允许改变方法,禁止被缓存
307 临时重定向,不允许改变方法,禁止被缓存
308 永久重定向,不允许改变方法
如果是
return URL;
,此时状态码默认是 302。
server 与 location 块下的 return 指令关系?
server {
server_name return.example.com;
listen 8080;
root html/;
reutrn 403;
location / {
return 404 "find nothing!";
}
}
return 403;
是在 server_rewrite 阶段,return 404 "find nothing!";
是在 rewrite 阶段,server_rewrite 阶段先处理,return 指令生效后,后面的指令都不会再执行,并且 return 指令是动作类指令,不会发生继承合并,所以上面的配置会返回 403。
return 与 error_page 指令的关系?
server {
server_name return.example.com;
listen 8080;
root html/;
error_page 404 /403.html;
location / {
return 404 "find nothing!\n";
}
}
上面的配置会返回状态码 404,返回的内容是 find nothing!,error_page 不会生效。
rewrite 指令
使用反向代理解决跨域时,用到了 rewrite 指令。rewrite 模块提供出来的指令还包括 set、if、break、return。
rewrite 是脚本类型的指令。语法是 rewrite regex replacement [flag];
,当 replacement 以 http://或者 https://或者$schema 开头,则直接返回 302 重定向。
rewrite 利用正则表达式和标志位实现 uri 重写和重定向,正则表达式可以用小括号进行变量提取。rewrite 只能放在 server、location 上下文和 if 判断中,并且只能对域名后边的除去传参外的字符串起作用。如果想对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用 proxy_pass 反向代理。
例如 http://microloan-sms-platform.yxapp.xyz/proxy/sms/task/querydeleted?page=1&pagesize=10
只能对 /proxy/sms/task/querydeleted 进行重写。
rewrite 规则后边,通常会带有 flag 标志位:
last:表示持续,用 replacement 这个 URI 进行新的 location 匹配
break:停止当前脚本指令的执行,等价与独立的 break 指令
redirect:返回 302 临时重定向,地址栏会显示跳转后的地址
permanent:返回 301 永久重定向,地址栏会显示跳转后的地址
last 和 break 的区别:
last 一般写在 server 和 if 中,而 break 一般使用在 location 中
last 不终止重写后的 uri 匹配,即新的 uri 会再从 server 走一遍匹配流程,而 break 终止重写后的匹配
break 和 last 都能阻止继续执行当前上下文后面的 rewrite 指令。
rewrite 和 return 的关系?
不带 flag 的 rewrite,会跟 return 顺序执行,并以 return 结果为准。如果带了 last 会跳过后面的 return 执行,如果带了 break ,会阻止后面的 return 执行。
3、find_config 阶段
此阶段主要是 location 匹配,可以查看 location 配置
4、preaccess 阶段
limit_conn 模块
模块的功能是限制每个客户端的并发连接数,默认编译进 Nginx,生效范围:
全部 worker 进程(基于共享内存)
进入 preaccess 阶段前不生效
并发连接数限制的有效性取决于 key 的设计:一般使用客户端的 ip,所以就需要依赖 postread 阶段的 realip 模块取到真实的 ip
limit_conn_zone 指令
定义共享内存(包括大小),以及 key 关键字。
语法 limit_conn_zone key zone=name:size;
只能在 http 上下文中使用
limit_conn 指令
限制并发连接数。
语法 limit_conn zone number;
可以用在 http,server,location 上下文中。
limit_conn_log_level 指令
限制发生时的日志级别,可选的值是 info|notice|warn|error,默认是 error。
limit_conn_status 指令
限制发生时向客户端返回的错误码,默认是 503。
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
server_name limit.example.com;
root html/
error_log logs/error.log info;
location / {
limit_conn_status 500; #超限后的状态码
limit_conn_log_level warn;
limit_conn addr 1;
limit_rate 50; #限制速度
}
}
limit_req 模块
功能是限制每个客户端的每秒处理请求数,默认编译进 Nginx,生效算法是 leaky bucket 算法(漏桶接水把不同的流量转换成均匀的流量),生效范围也是全部 worker 进程(基于共享内存),进入 preaccess 阶段前不生效。
limit_req_zone 指令
定义共享内存(包括大小),以及 key 关键字和限制速率,语法 limit_req_zone key zone=name:size rate=rate;
,只能在 http 上下文中使用。rate 的单位为 r/s(每秒请求)或者 r/m(每分钟请求)。
limit_req 指令
限制并发连接数,语法 limit_req zone=name [burst=number] [nodelay];
,burst 默认为 0(即漏桶的容量),nodelay,对 burst 中的请求不再采用延时处理的做法,而是立即处理。
limit_req_log_level 指令
限制发生时的日志级别
limit_req_status 指令
限制发生时向客户端返回的错误码,默认为 503。
limit_req 与 limit_conn 配置同时生效时,哪个有效?
limit_req 生效时间在 limit_conn 之前,所以只会看到 limit_req 的配置结果。
5、access 阶段
access 模块
限制某些 IP 地址的访问,默认编译进 Nginx,生效范围是:进入 access 之前不生效。
allow 指令
允许哪些地址访问,语法 allow address|CIDR|unix:|all;
,可以用在 http,server,location,limit_except 上下文中
deny 指令
禁止哪些地址访问,语法 deny address|CIDR|unix:|all;
,可以用在 http,server,location,limit_except 上下文中
location / {
deny 192.168.1.1;
allow 192.168.1.0/24;
allow 2001:0db8::/32;
deny all;
}
auth_basic 模块
基于 HTTP Basic Authutication 协议进行用户名密码的认证,模块默认编译进 Nginx。
auth_basic 指令
可以设置 string|off,默认为 off
auth_basic_user_file
用户名和密码配置在哪个文件中。
生成密码文件可以使用生成工具 htpassed,安装依赖包 httpd-tools,然后执行 htpasswd -c file -b user pass
auth_request 模块(第三方模块)
原理是收到请求后,先把请求 hold,然后生成子请求,通过反向代理技术把请求传递给上游服务。向上游的服务转发请求,若上游服务返回的响应码是 2xx,则继续执行,若上游服务返回的是 401 或 403,则将响应返回给客户端。默认没有编译进 Nginx。
auth_request 指令
语法 auth_request uri | off;
,默认为 off。
auth_request_set 指令
语法 auth_request_set $variable value;
可以根据上游返回的变量来设置新的变量。
satisfy 指令
限制所有 access 阶段模块,例如 access 模块、auth_basic 模块、auth_request 模块等,语法 satisfy all | any;
,如果是 all 就表示必须所有配置的 access 模块都通过才能使 access 阶段放行,any 只要有一个 access 模块的配置通过就能在 access 阶段放行。
precontent 阶段
try_files 指令
指令来自 try_files 模块,可以用在 server,location 上下文中,语法是:
try_files file... uri;
try_files file... =code
try_files 的作用是依次试图访问多个 url 对应的文件(由 root 或者 alias 指令指定),当文件存在时直接返回文件内容,如果所有文件都不存在,则按最后一个 uri 结果或者状态码 code 返回。
mirror 模块
处理请求时,生成子请求访问其他服务(copy 一份流量),对子请求的返回值不做处理。模块默认编译进 Nginx。
mirror 指令
可以设置 uri | off ,默认为 off。
mirror_request_body
指定是否需要把请求中的 body 转发给上游服务,默认为 on。
content 阶段
root 指令
将 url 映射为文件路径,以返回静态文件内容。默认值为 root html
,可以用在 http,server,location,if in location 上下文中。
root 会将完整 url 映射进文件路径中。
location /root {
root html;
}
访问:example.com/root/1.txt 文件路径为:html/root/1.txt
alias 指令
将 url 映射为文件路径,以返回静态文件内容。没有默认值,只能用在 location 上下文中。
alias 只会将 location 匹配后的 url 映射到文件路径
location /alias {
alias html;
}
访问:example.com/alias/1.txt 文件路径为:html/1.txt
root 或 alias 配置中,访问目录时 URL 最后没有带 /
?
/
?static 模块实现了 root/alias 功能时,发现访问目标是目录,但 URL 末尾未加 /
时,会返回 301 重定向,自动加上 /
。
关于重定向后的跳转问题,可以根据 absolute_redirect、port_in_redirect、server_name_in_redirect 来进行配置。
content 阶段生成待访问文件的三个相关变量
request_filename 待访问文件的完整路径,包括文件名和扩展名
document_root 由 URI 和 root/alias 规则生成的文件夹路径
realpath_root 将 document_root 中的软链接等换成真实路径
location /RealPath/ {
alias html/realpath/ # 这是一个软链接指向的是 first 文件夹
}
访问 /RealPath/1.txt 时
request_filename 为 html/realpath/1.txt
document_root 为 html/realpath
realpath_root 为软链接指向的真实路径 html/first
静态文件返回时的 content-tpe 相关
types 指令,语法
types {...};
,对文件扩展名和文件类型的映射default_type 指令,语法
default_type mime-type;
默认为 text/plain
index 模块
指定访问时返回 index 文件内容,语法 index file...;
默认是 index index.html;
。index 是优先于 auto_index 处理的。
auto_index 模块
当 URL 以 /
结尾时,尝试以 html/xml/json/jsonp 等格式返回 root/alias 中指向目录的文件。默认编译进了 Nginx。
log 阶段
log 模块就是把 HTTP 请求相关信息记录到日志中,并且 ngx_http_log_module 无法禁用。
log_format 指令
语法 log_format name [escape=default|json|none] string ...;
默认是 log_format combined "...";
默认的 combined 日志格式:log_format combined '$remote_addr - $remote_user [$time_local]' '"$request" $status $body_bytes_sent' '"$http_referer""$http_user_agent"';
access_log 指令
语法:access_log off;
或 access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]]
默认值为 access_log logs/access.log combined;
path 路径可以包含变量:不打开 cache 时每记录一条日志都需要打开、关闭日志文件,可以通过 open_log_file_cache 对日志文件名包含变量时进行优化
if 通过变量值控制请求日志是否记录
日志缓存
功能 批量将内存中的日志写入磁盘
写入磁盘的条件
所有待写入磁盘的日志大小超出缓存大小,即 buffer 的大小
达到 flush 指定的过期时间
worker 进程执行 reopen 命令,或者正在关闭
日志压缩
功能 批量压缩内存中的日志,在写入磁盘
buffer 大小默认为 64KB
压缩级别默认为 1(1 最快压缩率最低,9 最慢压缩率最高)
HTTP 过滤相关的模块处理过程
Nginx 的过滤模块就是对 HTTP 请求进行加工处理的。
接收 HTTP 头部后
会先经过 preaccess 阶段的处理
首先是 limit_req 模块
其次是 limit_conn 模块
然后经过 access 阶段,access 相关模块会进行处理
然后经过 content 阶段,access pass 后的内容,会先经过 concat 模块处理,然后是 static 模块处理
static 模块处理后的内容,就进入了响应阶段,会经过 header 过滤模块,先经过 image_filter 模块,然后经过 gzip 模块,此时会发送 HTTP 头部
然后就进入到响应 body 的处理中,同样的 image_filter 先处理,然后 gzip 后处理,处理完成后就可以发送 HTTP 响应包体了。
响应的包体会通过以下模块加工响应内容
copy_filter 复制包体内容,必须在 gzip 之前
postpone_filter 处理子请求
header_filter 构造响应头部,例如会添加 server,Nginx 版本号等等
write_filter 发送响应
Last updated
Was this helpful?