Nginx学习笔记
最近在系统的学习 Nginx 知识,主要看 陶辉 老师极客时间的课程,下面是整理的一部分笔记。
初识Nginx
Nginx主要应用场景
-
静态资源服务: 通过本地文件系统提供服务。比如一些 HTML、js、css 静态文件,使用 Nginx 处理会非常高效。
-
反向代理服务: 由于应用服务会处理大量业务,它的访问效率一般比较低,因此需要将应用服务组成一个集群,由性能强大的 Nginx 提供反向代理来访问。同时 Nginx 还提供负载均衡和缓存加速功能。
-
API服务: Nginx 也能提供 API 服务来处理业务,如:OpenResty,可以直接访问数据库、缓存服务等,而不需要经过应用服务。从而实现高性能的访问。
Nginx的主要优点
- 高并发、高性能
- 可扩展性好
- 高可靠性:可持续不间断运行数年。
- 热部署:可以在不停止服务的情况下重启服务,保证服务的可靠性。
- BSD许可证:Nginx 开源免费,可修改源代码实现定制化
Nginx组成部分
- Nginx 编译可执行文件:由各源码编译出的一个文件
- nginx.conf 配置文件:控制 Nginx 的行为
- access.log 访问日志:记录每一条 http 请求信息
- error.log 错误日志:定位问题
Nginx命令行
- 格式:
sbin/nginx -s reload
- 帮助:
-? -h
- 使用指定的配置文件:
-c
- 指定配置指令:
-g
- 指定运行目录:
-p
- 发送信号:
-s
- 立刻停止服务:stop
- 优雅的停止服务:quit
- 重载配置文件:reload
- 重新开始记录日志文件:reopen
- 测试配置文件是否有语法错误:
-t -T
- 打印nginx的版本信息、编译信息等:
-v -V
用免费的SSL证书实现一个HTTPS站点
下载certbot,一个python工具
yum install python2-certbot-nginx
为指定目录下(默认/usr/local/nginx/conf/
目录)的配置文件生成指定域名的HTTPS证书
certbot --nginx --nginx-server-root=/usr/local/nginx/conf/vhost/ -d turbobin.site
Nginx架构基础
Nginx的请求处理流程
当外部流量(web、Email、TCP流量)进入 Nginx 时,会经过非阻塞事件驱动引擎,引擎包括三个状态机,即处理 TCP 流量的传输层状态机、处理 HTTP 请求的 HTTP 状态机、处理 email 的 MAIL 状态机。当 nginx 处理静态资源或作为反向代理时,可以把资源缓存在磁盘中,当服务器内存已不足以缓存住所有的文件,aIO 的调用会退化成阻塞的磁盘调用,此时会使用线程池来处理磁盘的阻塞调用。
对每个处理完成的请求,Nginx 会记录 access 访问日志和 error 错误日志。当然也可以使用 syslog 传输到远程的 log 机器上,方便统一分析处理日志。
Nginx 也可以通过负载均衡,把请求以协议的方式,如 HTTP、Mail、stream(TCP)代理,转发给下游的应用服务器;另外一种方式是通过 FastCGI、uWSGI、SCGI,memcached 代理方式与下游服务器连接。
Nginx的进程结构
Nginx 采用多进程的方式,即一个 master 进程,多个子 worker。其中 master 只是作为 worker 进程的管理,只有worker 进程才真正地处理请求。一般建议把 worker 进程的个数设置为 CPU 核数。
除了 worker 进程,还有处理缓存使用的 Cache manager、Cache loader 进程。
使用信号管理Nginx进程
Nginx 管理进程是通过信号的方式来管理的。
master 进程:
-
监控worker进程:CHLD,
kill -SIGCHLD $PID
-
管理worker进程
- 立即关闭:TERM, INT
- 优雅的退出:QUIT
- 重启:HUP
- 重新打开文件:USR1
- USR2:新产生一个新的 master 进程和它的新的 worker 进程
- WINCH:让 master 进程优雅的关闭它的所有 worker 进程。
work进程
- 接收信号
- TERM, INT
- QUIT
- USER1
- WINCH
nginx 命令行
以上管理进程的信号对应了 Nginx 的几个命令行
- reload:HUP
- reopen:USR1
- stop:TREM
- quit:QUIT
优雅的关闭worker进程是怎样的过程
1、设置定时器(worker_shutdown_timeout)
2、关闭监听句柄
3、关闭空闲连接
4、在循环中等待全部连接关闭
5、退出进程
reload 流程
每当修改配置文件时需要执行 reload 命令,Nginx reload 命令可以平滑的重启服务,它执行的流程如下:
- 向 master 进程发送 HUP 信号(reload 命令)
- master 进程校验配置语法是否正确(相当于执行 nginx -t)
- master 进程打开新的监听端口
- master 进程用配置启动新的 worker 子进程
- master 进程向老 worker 子进程发送 QUIT 信号
- 老 worker 进程关闭监听句柄,处理当前连接后结束进程
Nginx升级流程
线上执行 nginx 升级不能简单的先停止服务,再重启,这样会使大量的请求无法响应,安全的做法是使用热升级。
1、将旧的 nginx 二进制文件换成新的(先备份旧的)
2、向 master 进程发送 USR2 信号,这时候会新起一个 master 进程,新的 master 进程会产生新的 worker 进程,新产生的连接请求会由新的 master 进程和新的 worker 进程处理。
3、master 进程修改 pid 文件名,加后缀 .oldbin
4、master 进程用新的 nginx 文件启动新 master 进程
5、向老 master 进程发送 QUIT 信号,关闭老 master 进程
6、回滚操作:向老 master 发送 HUP 信号,重新拉起 woker 进程,向新 master 发送 QUIT
Nginx的事件驱动模型
nginx 启动时,处于等待连接状态,对应于epoll 中的epoll_wait方法(阻塞方法),worker 进程处于睡眠状态。
当有 TCP 连接过来,操作系统处理完握手过程后,会通知 epoll_wait 方法往下执行,同时唤醒 worker 进程。操作系统会把准备好的事件放在事件队列中,epoll 从队列中把一个个事件取出来处理。
同步和异步、阻塞和非阻塞
同步和异步是描述的代码的执行顺序,或者说代码处理数据的逻辑状态。
同步 指进程调用接口时,需要等待接口处理完数据并返回响应才继续执行。
异步 指的是进程调用接口后,不必等待数据准备好,可以继续执行,后续数据准备好可以通过一定的方法获得,比如回调。
阻塞和非阻塞描述的是进程等待结果状态,是否会进行进程间切换。
阻塞 指的是进程等待接收数据处理结果时,如果数据没有准备好,当前进程会被挂起,操作系统会切换到另外一个进程,直到有数据返回时,再切换回来,当前进程才会被唤醒。
非阻塞 指的是进程等待接收数据处理结果时,如果数据没有准备好,直接返回一个 err 状态,之后会再次轮询请求,直到数据被处理完成并返回。
Nginx模块分类
nginx 模块主要集中在 ngx_core_module
中,其中包括:
-
事件处理模块:
ngx_event_code_module
-
HTTP处理模块:
ngx_http_core_module
- 邮件处理模块:
ngx_mail_core_module
- 流处理模块:
ngx_stream_core_module
此外还有其他如解析配置的处理模块(ngx_conf_module
)等
Nginx连接池
nginx 连接池由两部分构成:对下游客户端的连接、对上游服务器的连接。
连接池的大小影响了 nginx 的并发性。
{
woker_connections 512; # 默认512大小的连接池数组,一般需要改大一点
}
Nginx内存池
nginx 的内存池分为连接内存池和请求内存池,一般会预分配一定的大小。
连接内存池
与 nginx 建立 TCP 连接都需要分配内存,如果频繁的建立、断开连接就需要频繁的为连接分配内存,而操作系统分配内存是有代价的,所以 nginx 设置了连接内存池。对于长连接(keep-alive)而言,一个 TCP 连接可能运行很多 HTTP 连接,只要 TCP 连接不断开,这个连接的内存就不会释放。
{
connection_pool_size 256|512; # 预分配大小,不同的操作系统可能不一样。
}
当连接超过预分配大小时,操作系统会再次分配同样的内存的大小。
请求内存池
默认预分配大小:4k。因为对连接而言,需要保存大量的上下文信息,如 HTTP 请求的 url 比较长,header 比较大,需要保存下来,就需要比较大的内存。
{
request_pool_size 4k;
}
Nginx进程间的通讯方式
进程间的通讯可以使用以下两种方式:
- 信号
- 共享内存:共享内存的使用涉及锁和内存管理器 slab
如何使用Slab内存管理器?
需要下载 tengine 源码,slab 模块在 tenine-x.x.x/modules/ngx_slab_stat/
目录下。在编译开源 nginx 时,可以手动把模块添加进来:
nginx-1.14.0 $ ./configure --add-module=../tenine-x.x.x/modules/ngx_slab_stat/
然后执行 make,就可以把模块安装进来了。
详解HTTP模块
HTTP请求的完整流程
操作系统内核与客户端经过三次握手建立 TCP 连接 ——>
nginx 内部负载均衡算法选中 CPU 上的 worker 进程 ——>
nginx 事件处理模块中的 epoll_wait
读取连接(读事件) ——>
accept
方法,分配连接内存池(connection_pool_size: 512) ——>
转交请求给 HTTP 模块,设置超时定时器,分配请求内存池,读取 URI、header ——>
开始 11 个阶段的 http 请求处理。
接收请求的事件模块:
接收请求的HTTP处理模块:
HTTP请求处理的11个阶段:
指令的覆盖
指令分为两类:
值指令:存储配置项的值,如:root、access_log、gzip,这类指令可以合并
动作类指令:指定行为,如:rewrite、proxy_pass,这类指令不可以合并
值指令的指令合并规则:向上覆盖
- 子配置不存在时,直接使用父配置块
- 子配置存在时,直接覆盖父配置块
listen指令的用法
listen unix:/var/run/nginx.sock;
listen 127.0.0.1:8080;
listen 127.0.0.1; # 默认监听80端口
listen 8080;
listen *:8080;
listen localhost:8080 bind;
listen [::]:8080 ipv6only=on;
listen [::1];
server_name指令的用法
server_name指令:
-
指令后可以跟多个域名,第一个是主域名
server { server_name primary.turbobin.com second.turbobin.com; server_name_in_redirect on; # 默认off return 302 /redirect; }
当
server_name_in_redirect
打开时,访问http://second.turbobin.com
,会返回主域名http://primary.turbobin.com/redirect
,关闭时(off),会重定向到http://second.turbobin.com/redirect
-
*
泛域名:仅支持在最前或最后。如:server { server_name *.turbobin.com; }
-
正则表达式:加
~
前缀一般用法
server { server_name www.turbobin.com ~^www\d+\.turbobin\.site$; }
用正则表达式创建变量:用小括号()
server { server_name ~^(www\.)?(.+)$; location / { root /site/$2; } }
server { server_name ~^(www\.)?(?<domain>.+)$; location { root /site/$domain; } }
-
其他
.turbobin.com
可以匹配turbobin.com, *.turbobin.com
_
可以匹配所有""
匹配没有传递Host头部的请求
server匹配的顺序
1、精确匹配
2、*
在前的泛域名
3、*
在后的泛域名
4、按文件中的顺序匹配正则表达式域名
5、default server
- 第一个
- listen 指定了 default
Rewrite模块的 return 指令
返回状态码
- 444:nginx 自定义,表示关闭连接
- 301:HTTP1.0 永久重定向
- 302:HTTP1.0 临时重定向,禁止被缓存
- 303:HTTP1.1 临时重定向,允许改变方法(GET、POST等方法),禁止被缓存
- 307:HTTP1.1 临时重定向,不允许改变方法,禁止被缓存
- 308:HTTP 永久重定向,不允许改变方法
error_page指令
示例用法
1、error_page 404 /404.html;
2、error_page 500 502 503 504 /50x.html;
3、error_page 404 =200 /empty.gif;
4、error_page 404 = /404.php;
5、
location / {
error_page 404=@fallback;
}
location @fallback {
proxy_pass http://backend;
}
6、error_page 403 http://example.com/forbidden.html;
7、error_page 404 =301 http://example.com/notfound.html;
rewrite 指令:重写URL
用法:rewrite regex replacement [flag]
日志:rewrite_log on;
- 将 regex 指定的url替换成replacement这个新的url
- 当replacement以http:// 或者https:// 或者$schema开头,则直接返回302重定向
- 替换后的url根据指定的方式进行处理
- – last:用replacement这个url进行新的location匹配
- – break:break指令停止当前脚本指令的执行,等价于独立的break指令
- – redirect:返回302重定向
- – permanent:返回301重定向
条件判断:if 指令
location指令
location 匹配规则:仅匹配 URL,忽略参数。
合并连续的 /
符号
- merge_slashes on;
用于内部跳转的命名 location
- @xxx
前缀字符串
- 常规
- 精确匹配:=
- 匹配上后则不再进行正则表达式匹配:^~
正则表达式
- 大小写敏感的正则匹配:~
- 忽略大小写的正则匹配:~*
location 的匹配顺序
限制客户端的并发连接数 limit_conn
生效阶段:NGX_HTTP_PREACCESS_PHASE 阶段
模块:http_limit_conn_module
默认编译进 nginx,通过 –without-http_limit_conn_module 禁用
生效范围:
- 全部 woker 进程(基于共享内存)
- 进入 preaccess 阶段前不生效
- 限制的有效性取决于 key 的设计:依赖 postread 阶段的 realip 模块取到真实的 ip
步骤
-
定义共享内存(包括大小),以及 key 关键字
语法:
limit_conn_zone key zone=name:size;
key 一般为
$binary_remote_addr
,$remote_addr
-
限制并发连接数
语法:
limit_conn name number;
限制发生时的日志级别
语法:limit_conn_log_level info|notice|warn|error;
默认日志级别是 error,可以到 error .log 日志中查看。
限制发生时向客户端返回的错误码
语法:limit_conn_status code;
默认返回 503。
限制返回速率
语法:limit_rate byte;
如 limit_rate 50;
每秒返回50字节。
限制客户端每秒处理请求数 limit_req
生效阶段:NGX_HTTP_PREACCESS_PHASE 阶段
模块:http_limit_req_module
默认编译进 nginx,通过 –without-http_limit_conn_module 禁用
生效算法:leaky bucket 算法
生效范围:
- 全部 woker 进程(基于共享内存)
- 进入 preaccess 阶段前不生效
- 限制的有效性取决于 key 的设计:依赖 postread 阶段的 realip 模块取到真实的 ip
步骤
-
定义共享内存(包括大小),以及 key 关键字和限制速率
语法:
limit_req_zone key zone=name:size rate=rate;
rate 单位为 r/s 或 r/m(每秒或每分钟处理多少个请求) -
限制并发连接数
语法:
limit_req zone=name [burst=number][nodelay];
burst 相当于一个缓存桶,当请求的数量过多时,第一个请求被处理,其他的请求放入缓存区,当缓存区达到上限时,则返回错误。
burst 默认为 0,nodelay 表示对 burst 中的请求不再采用延时处理的做法,而是立刻处理。
限制发生时的日志级别
语法:limit_req_log_level info|notice|warn|error;
默认日志级别是 error,可以到 error .log 日志中查看。
限制发生时向客户端返回的错误码
语法:limit_req_status code;
默认返回 503。
Access 模块:限制 ip 的访问
生效阶段:NGX_HTTP_ACCESS_PHASE 阶段
模块:http_access_module
默认编译进 nginx,通过 –without-http_access_module 禁用功能
生效范围:进入 access 阶段前不生效
语法:
允许访问:allow address|CIDR|unix:|all;
禁止访问deny address|CIDR|unix:|all;
示例:
location / {
deny 192.168.1.1;
allow 192.168.1.0/255;
allow 10.1.1.0/16;
allow 2001:0db8::32;
deny all;
}
auth_basic:限制网站登录的用户名和密码
基于 HTTP Basic Authutication 协议进行用户名密码的认证。
默认编译进 nginx,可以通过--without-http_auth_basic_module
禁用此功能。
指令:
auth_basic string|off;
auth_basic_user_file;
指定密码文件
示例:
location {
satisfy any;
auth_basic "Auth Access";
auth_basic_user_file /usr/local/nginx/auth/passwd;
deny all;
}
生成密码文件的工具:yum -y install httpd-tools
新增:htpasswd -c /usr/local/nginx/auth/passwd -b user password
添加:去掉 -c
auth_request:第三方鉴权模块
如果需要管理比较多的用户名密码,用 auth_basic 就比较繁琐了,nginx 提供了一个统一的用户权限验证系统:auth_request 模块。
功能:向上游服务转发请求,若上游服务响应码是 2xx,则继续执行,若上游服务返回的是 401 或者 403,则将响应返回给客户端。
原理:收到请求后,生成子请求,通过反向代理技术把请求传递给上游服务。
auth_request 模块默认未编译进 nginx,需要通过 --with-http_auth_request_module
编译进来。
指令:
auth_request uri|off;
默认 of。
auth_request_set $variable value;
示例:
location / {
auth_request /test_auth;
}
location = /test_auth {
proxy_pass http://127.0.0.1:8090/auth_upstream;
proxy_pass_request_body off;
proxy_pass_header Content_Length "";
proxy_pass_header X-Original-URI $request_uri;
}
限制所有access阶段模块的satisfy指令
语法:satisfy all | any;
默认 all
- 如果不开启
satisfy any;
,则 access 阶段的模块会按模块顺序执行(access - auth_basic - auth_request),其中一个模块拒绝请求,则下一个模块没有机会得到执行。 - 如何设置
satisfy any;
,则 access 阶段的任何一个模块接受了请求,则下一个access模块的任何设置都会变为无效。意思是 只要满足任何一个 access 模块的条件即可。
try_files 指令:按序访问资源
语法:
-
try_files file1 file2 ... uri;
-
try_files file1 file2 ... =code;
模块:ngx_http_try_files_module
,默认编译进 nginx 中
功能:依次试图访问多个 url 对应的文件(由 root 或 alias 指定),当文件存在时,直接返回文件内容,如果所有的文件都不存在,则按最后一个 url 结果或者 code 返回。
示例:
location /first {
try_files /system/maintenance.html
$uri $uri/index.html $uri.html
@lasturl; # 当前面的文件都不存在时,返回@lasturl定义的返回
}
location @lasturl {
return 200 'last url';
}
location /second {
try_files $uri $uri/index.html $uri.html =404;
}
实时拷贝流量:mirror模块
模块:ngx_http_mirror_module
,默认编译进了 nginx
作用:处理请求时,生成子请求访问其他服务,对子请求的返回值不做处理
语法:
mirror uri | off;
默认为 offmirror_request_body on | off;
默认 on
mirror 请求的响应会被直接丢弃。
示例:
在一台 nginx 中
server {
listen 8001;
location / {
mirror /test_mirror;
mirror_request_body off;
}
location = /test_mirror {
internal; # 表示只允许内部请求
proxy_pass http://127.0.0.1:10020$request_uri;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}
在另一台 nginx 中
server {
listen 10020;
location / {
return 200 'mirror response'; # 这里实际并不会返回
}
}
当请求 8001 端口的 uri 时,会同时将流量传入到 10020 端口上。
mirror 模块通常用于将生产环境的部分流量传入到测试环境中。
详解 root 和 alias 指令 (content 阶段)
功能:将 url 映射为文件路径,以返回静态文件内容。
差别:
- root 会将完整的 url 映射进文件路径中 (即 path + url),alias 只会将 location 后的 url 映射到文件路径。
- root 有默认值为
root html
, alias 没有默认值 - root 可以出现在 http、server、location、if inlocation 等模块中,alias 只能出现在 location 模块下
static 模块提供的几个变量
生成待访问文件的三个相关变量:
$request_filename
:待访问文件的完整路径$document_root
:由 url 和 root/alias 规则生成的文件夹路径$realpath_root
:将$document_root
中的软连接替换成真实的路径
示例:
location /realpath/ {
alias html/realpath/;
return 200 '$request_filename:$document_root:$realpath_root'
}
其中 realpath 目录 软连接到了 first 目录: realpath -> first
当访问 realpath/1.txt
时,返回:
/usr/local/nginx/html/realpath/1.txt:/usr/local/nginx/html/realpath/:/usr/local/nginx/html/first
静态文件放回的 content-type
- type 指令:
type {...}
,默认type {text/html;image/gif gif;image/jpeg jpg;}
- default_type 指令,默认
default_type text/plain;
types_hash_bucket_size size;
默认 size 64types_hax_max_size size;
默认 size 1024
未找到文件时的错误日志
log_not_foud on | off;
默认 on,在生产环境可以设置为关闭,减少对 error.log 文件的 io 操作。
URL不以斜杆结尾却能访问目录的做法
static 模块对的 3 个指令:
server_name_in_redirect on | off;
默认 off,表示重定向后是否返回 server_name 名称给客户端 header 中的 Location。port_in_redirect on | off;
默认 on,表示是否返回端口给客户端的 header 中的 Location 中。absolute_redirect on | off;
默认 on,表示是否返回域名+端口+url
示例:
server {
listen 8088;
server_name turbobin.com turbobin.site;
server_name_in_redirect off;
port_in_redirect on;
absolute_redirect off;
root html/; # html目录下有个first文件夹
}
-
当发送
curl localhost:8088/first -I
时,将返回 301 永久重定向,Location 中显示/first/
-
把
absolute_redirect off;
改为 on,将返回 301 重定向,Location 中显示http://localhost:8088/first/
,当访问
curl -H 'Host: aaa' localhost:8088/first -I
时,Location 中将显示http://aaa.8088/first/
-
把
server_name_in_redirect off;
改为 on,将返回 301 重定向,Location 中显示http://turbobin.com:8088/first/
index 和 autoindex
index
模块:ngx_http_index_module
,默认编译进了 nginx 二进制文件中。
功能:指定/访问时返回 index 文件的内容,默认为 index index.html;
autoindex
模块:ngx_http_autoindex_module
,默认编译进 nginx。
功能:当 URL 以 /
结尾时,尝试以 html/xml/json/jsonp 等格式返回 root/alias 中指向目录的结构。
指令:
autoindex on | off;
默认为 off,总开关。autoindex_exact_size on | off;
默认为 on,显示文件的单位,表示显示文件的字节数(on)还是相对单位(off)(当格式为 html 返回时才有效)autoindex_format html | xml | json | jsonp;
默认为 html,表示以什么方式返回目录结构autoindex_localtime on | off;
默认为 off,表示是否显示当地时间。
index 模块先于 autoindex 模块执行,所以当 index 和 autoindex 同时指定,且 index 指定的文件存在时,autoindex 的配置会失效。由于 index 模块不能移除,所有为了让 autoindex 指令生效,只能使 index 指定的文件不存在。
concat 指令:提升访问多个小文件的性能
功能:当需要访问多个小文件时,把它们的内容合并到一次 HTTP 响应中返回,提升性能。
模块:ngx_http_concat_module
,此模块在阿里开发 Tengine
版本中,需要下载 Tengine
源码,通过 ./configure --add_module=../nginx-http-concat/
手动编译添加进来。
使用方式:在 URL 后加上 ??
后,通过多个逗号 ,
分割文件。如果还有参数,则在最后通过 ?
添加参数。
指令:
concat on | off;
默认 offconcat_types MIME types;
默认concat_types text/css application/x-javascript;
,指定对哪些文件的内容做合并。concat_unique on | off;
默认 on,表示是否对一种文件类型还是多种文件类型的内容进行合并。concat_max_files number;
默认最大 10 个文件concat_delimiter string;
指定返回多个文件内容之间的分隔符,默认没有分隔符concat_ignore_file_error on | off;
默认 off,表示是否忽略文件中错误
access 日志的详细用法
功能:将 HTTP 请求的相关信息记录到日志中
模块:ngx_http_log_module
,默认编译到 nginx 中,无法禁用。
access 日志格式:log_format
Syntax: log_format name [escape=default/json/none] string...;
Default: log_format combined "";
Context: http
默认的 access 格式:
http {
log_format combined '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
}
配置日志文件路径:access_log
Syntax: access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
access_log off;
Default: access_log logs/access.log combined;
Context: http, server, location, if in location, limit_except
- path 路径可以包含变量,意味着每次请求的路径都可能不同,如果不打开 cache ,每记录一条日志都需要打开、关闭日志文件,性能会比较糟糕
- if 通过变量值控制请求是否记录日志中
- 日志缓存:
- 功能:批量将内存中的日志写入磁盘
- 写入磁盘的条件
- 所有待写入磁盘的日志大小超过缓存大小 (通过 buffer 设置)
- 达到 flush 指定的过期时间(通过 flush=time 设置)
- woker 进程执行 reopen 指令,或者正在关闭
- 日志压缩:
- 功能:批量压缩内存中的日志,再写入磁盘
- buffer 大小默认为 64kb
- 压缩级别默认为 1 (1 为最快压缩率最低,9 最慢 压缩率最高)
对日志文件名包含变量时的优化:
Syntax: open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
Default: open_log_file_cache off;
Contex: http, server, location
- max :缓存内的最大文件句柄数,超出后用 LRU 缓存淘汰算法
- inactive:文件访问完后这段时间内不会被关闭,默认 10 秒
- min_uses:在 inactive 时间内使用次数超过 min_uses 才会继续存在内存中,默认 1
- valid:超过 valid 时间后将对缓存的日志文件检查是否存在,默认 60 s
- off: 关闭缓存功能
HTTP 过滤模块的调用流程
HTTP 过滤模块主要对 http 响应内容做处理,比如对返回的图片进行压缩。以下是过滤模块所处的位置:
替换响应中的字符串:sub 模块
模块:ngx_http_sub_filter_module
默认未编译进 nginx ,通过 –with-http_sub_module 启用
功能:将响应中指定的字符串替换成新的字符串
指令:
sub_filter aaa bbb;
把aaa
字符串替换成bbb
,注意aaa
是可以忽略大小写的。sub_filter_last_modified on | off;
默认 off,表示是否在http头部返回Last-Modified
字段(最后修改时间)sub_filter_once on | off;
默认 on,表示是否只替换第一个字符串。sub_filter types text/html ...;
对哪种或哪几种类型的文件做替换,当设置为*
时,表示对所有文件类型都替换。
在 HTTP 响应前后添加内容:addition 模块
模块:ngx_http_addition_filter_module
,默认未编译进 nginx ,需要 –with-http_addtion_module 启用
功能:在响应前或响应后增加内容,而增加内容的方式是通过增加子请求的响应完成的
指令:
add_before_body uri;
在 body 之前添加内容add_after_body uri;
在 body 之后添加内容addtion_types mime-type ...;
对什么类型的文件添加内容,默认为text/html
Nginx 的变量
nginx 针对变量的处理有两个模块:
-
提供变量的模块: Preconfiguration 中定义新的变量,解析出变量的方法
<——>
变量名 -
使用变量的模块:比如 http 的 access 日志,解析 nginx.conf 时定义变量使用方式
nginx 变量的特性:
- 惰性求值
- 变量值可以时刻变化,其值为使用那一刻的值
存放变量的哈希表:
variables_hash_bucket_size size;
默认 size 为 64 字节variables_max_bucket_size size;
默认 1024 字节
HTTP 框架提供的变量:
- HTTP 请求相关的变量
- TCP 连接相关的变量
- Nginx 处理请求过程中产生的变量
- 发送 HTTP 响应时相关的变量
- Nginx 系统变量
HTTP 请求相关的变量
参数 | 说明 |
---|---|
args_参数名 | URL中某个具体参数的值 |
args | 全部 URL 参数 |
query_string | 与 args 变量完全相同 |
is_args | 如果请求中有参数则返回? ,否则返回空 |
content_length | HTTP 请求中标识 body 长度的Content_Length头部的值 |
content_type | 标识请求包体类型的 Content_Type 头部的值 |
uri | 请求的 URI(不同于 URL,不包含 ? 后的参数) |
document_uri | 与 uri 完全相同,因为历史原因存在 |
request_uri | 请求的 URL,包括 URI 以及完整的参数 |
scheme | 协议名,例如 HTTP, HTTPS |
request_method | 请求方法,例如 GET , POST |
request_length | 所有请求内容的大小,包括请求行、头部、包体等 |
remote_user | 由 HTTP Basic Authentication 协议传入的用户名(auth_basic模块) |
request_body_file | 临时存放请求包体的文件 1. 如果包体非常小,则不会存文件 2. client_body_in_file_only 强制所有包体存入文件,且可决定是否删除 |
request_body | 请求中的包体,这个变量当且仅当使用反向代理,且设定用内存暂存包体时才有效 |
request | 原始的 url请求,含有方法与协议版本,如:GET /?a=1&b=22 HTTP/1.1 |
host | 1. 先从请求行中取 2. 如果含有 Host 头部,则用其值替换掉请求行中的 主机名 3. 如果前两者都取不到,则使用匹配上的 server_name |
其他相关的变量
http_头部名字
:返回一个具体请求头部的值
特殊:
http_host
http_user_agent
http_refere
http_via
http_x_forwarded_for
http_cookied
TCP 连接相关的变量
Nginx 处理请求过程中产生的变量
发送 HTTP 响应时相关的变量
Nginx 系统变量
防盗链:referer 模块
场景:某网站通过 url 引用了你的页面,当用户在浏览器上点击你的 url 时,http 请求的头部中会通过 referer 头部,将改网站当前的 url 带上,告诉服务器本请求是由这个页面发起的。
目的:拒绝非正常的网站访问我们的站点。
思路:通过 referer 模块,用 invalid_referer
变量根据配置判断 referer 头部是否合法。
模块:默认编译进 nginx ,通过 –without-http_referer_module 禁用
指令:
valid_referers none | blocked | server_names | strings...;
referer_hash_bucket_size size;
默认 64 字节referer_hash_max_size size;
默认 2048 字节
valid_referers指令
可同时携带多个参数,表示多个 referer 头部都生效。
- none:允许缺失 referer 头部的请求访问
- blocked:允许 referer 头部没有对应的值(即 referer 为空值)的请求的访问
- server_names
- 若 referer 中站点域名与 server_name 中本机域名某个匹配,则允许访问。
- 表示域名及 url 的字符串,对域名可在前缀或后缀中含有
*
通配符,若 referer 头部的值匹配后则允许访问 - 正则表达式:若 referer 头部匹配正则表达式后,则允许访问
invalid_referer变量
- 允许访问时,变量值为空
- 不允许访问时,变量值为 1
示例:
server {
server_name referer.turbobin.com;
error_log logs/my_error.log debug;
root html;
location / {
valid_referers none blocked server_names
*.turbobin.site www.turbobin.org/html/
~\.google\.;
if ($invalid_referer) {
return 403
}
return 200 'valid referer\n';
}
}
更安全的防盗链模块:secure_link
指令:
secure_link expression;
:secure_link_md5 expression;
:secure_link_secret word;
变量:
$secure_link
:值为空字符串,验证不通过;值为 0,表示URL过期;值为1,表示验证通过。$secure_link_expires
:时间戳的值。
示例:
原请求:
/test.txt?md5=md5_string&expires=时间戳
命令行生成 md5 值:
echo -n '时间戳+URL+客户端IP+密钥' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d
或者使用 python 生成 md5值
nginx 配置:
secure_link $arg_md5,&arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secete";
nginx 配置示例:
server {
server_name turbobin.com;
default_type text/plain;
location / {
secure_link &arg_md5,&arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secete";
if ($secure_link = "") {
return 403;
}
if ($secure_link = 0) {
return 410;
}
return 200 '$secure_link:&secure_link_expires \n';
}
}
仅对URL进行hash的简单办法:
原理:
- 将请求 URL 分成三个部分:/prefix/hash/link
- Hash 生成方式:对“link 密钥”做 md5 哈希求值
- 用
secure_link_secret secret;
指令配置密钥
示例:
原请求:link
生成安全的请求:/prefix/md5/link
命令行生成 md5:echo -n 'link+secret' | openssl md5 -hex
示例配置:
server {
server_name turbobin.com;
default_type text/plain;
location /prefix/ {
secure_link_secret my_secret;
if ($secure_link = "") {
return 403;
}
rewrite ^ /secure/$secure_link;
}
location /secure/ {
internal;
alias html/;
}
}
先生成 md5 值:
$ echo -n 'test1.txtmysecret' | openssl md5 -hex
(stdin)= 286323bc57bae84066d0b5031265159f
下面访问安全链接:
curl 'turbobin.com/prefix/286323bc57bae84066d0b5031265159f/test1.txt'
这时候可以访问到 html 目录下 test1.txt 文件的内容。
通过 map 模块映射新变量
模块:ngx_http_map_module
默认编译进了 nginx。
功能:基于已有变量,使用类似 switch {case: ... default: ...}
语法创建新变量,为其他基于变量值实现功能的模块提供更多可能性。
指令:
map &nginx_variable $variable {...};
第一个变量是 nginx 已有变量,第二个是自定义变量map_hash_bucket_size size;
根据系统不同,默认为 32 / 64 / 128 字节map_hash_max_size size;
默认 2048
规则:
已有变量:
- 字符串
- 一个或多个变量
- 变量与字符串组合
case 规则匹配顺序:
- 字符串严格匹配
- 使用 hostnames 指令,可以对域名使用前缀* 泛域名匹配
- 使用 hostnames 指令,可以对域名使用后缀* 泛域名匹配
- ~ 和 ~* 正则表达式匹配,后者忽略大小写
default 规则:
- 没有匹配到任何规则时,使用default
- 缺失 default 时,返回空字符串给变量
其他:
- 可以使用 include 语法提升可读性
- 使用 volatile 禁止变量值缓存
示例:
map $status $normal {
~^2 1;
~^3 1;
default 0;
}
map $status $abnormal {
~^2 0;
~^3 0;
default 1;
}
map $uri $need_report {
/banner/adstat 1;
default 0;
}
server {
listen 80;
server_name api.turbobin.com;
access_log logs/bad_access.log access if=$abnormal;
access_log logs/access.log main if=$normal;
access_log syslog:server=100.119.167.81:12304 graylog2_format_api if=$need_report;
}
实现 AB 测试:split_clients 模块
指令:split_client string $variable {...};
示例:
split_client "${http_userid}" $variant { # http_头部变量,表示从http头部取值
0.51% one;
20.0% two;
50.5% three;
10% four;
* "";
}
server {
listen 8089;
server_name split.turbobin.com;
location / {
return 200 'AB test num:$variant \n';
}
}
发送请求:
curl -H "userid: 234879815" split.turbobin.com:8089;
将会返回在百分比范围内对应的值。
geo 模块:根据客户端地址创建新的变量
模块:ngx_http_geo_module
,默认编译进了 nginx
功能:根据 IP 地址创建新的变量
语法:geo [$address] $variable {...}
规则:
- 如果 geo 指令不指定 $address,那么默认使用 $remote_addr 变量作为 IP 地址
{...}
内的指令匹配:优先最长匹配- 通过 IP 地址及子网掩码的方式,定义IP范围,当 IP 地址在范围内时,使用其后的参数值,配置 IP 地址范围重合时,使用最大范围的 IP 地址配置
- default 指定了当以上范围都未匹配时,新变量的默认值
- 通过 proxy 指令指定可信地址(参考 realip 模块),此时 remote_addr 的值为 X-Forwarded-For 头部值中最后一个 IP 地址
proxy_recuisive
允许循环地址搜索- 使用 include 优化可读性
- delete 删除指定网络
示例:
geo $country {
default ZZ;
#include conf/geo.conf;
proxy 120.24.165.101;
127.0.0.0/24 US; # 表示 127.0.0.1 ~ 127.0.0.255地址段
127.0.0.1/32 RU;
10.1.0.0/16 RU; # 表示 10.0.0.1 ~ 10.0.255.244地址段
192.168.1.0/24 UK;
}
server {
server_name geo.turbobin.com;
location / {
return 200 '$country\n';
}
}
注意事项:
24表示子网掩码:255.255.255.0
16表示子网掩码:255.255.0.0
8表示子网掩码:255.0.0.0
依次发送请求:
curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.1,192.168.1.123' geo.turbobin.com # 返回: UK
curl -H 'X-Forwarded-For: 10.1.0.0' geo.turbobin.com # 返回 RU
curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.1' geo.turbobin.com # 命中最长匹配,返回 RU
geoip 模块:获取用户的地理位置
基于 MaxMind 数据库从客户端地址获取变量:
指令:
geoip_contry file;
指定 MaxMind 安装生成的地址国家库,如geoip_country /usr/local/share/GeoIp/GeoIp.dat;
geoip_city file;
指定城市库,如geoip_city /usr/local/share/GeoIp/GeoLiteCity.dat;
geoip_proxy address | CIDR;
指定可信地址
与 conunty 有关的变量:
$geoip_country_code
:两个字母的国家代码,比如 CN 或 US$geoip_country_code3
:三个字母的国家代码,比如CHN或者USA$geoip_contry_name
:国家名称,如:China,United States
与 city 有关的变量:
代理 ip 网站,用于测试:http://www.goubanjia.com/
对客户端使用 keepalive 提升连接效率
功能:多个 HTTP 请求通过复用 TCP 连接实现以下功能
- 减少握手次数
- 通过减少并发连接数减少了服务器资源的消耗
- 降低 TCP 拥塞控制的影响
协议:
- Connection头部:取值为 close 或 keepalive,前者表示请求处理完即关闭连接,后者表示复用连接处理下一个请求
- Keep-Alive 头部:其值为 timeout=n,告诉客户端连接至少保留 n 秒
对客户端 keepalive 行为控制的指令:
keepalive_disable none | browser ...;
默认keepalive_disable msie6;
,表示对某些浏览器不使用 keepalivekeepalive_request number;
默认100,表示在一个 TCP 连接上最多可以使用多少个 http 请求keepalive_timeout timeout [header_timeout];
默认 75s,表示下一个 http 连接的超时时间
关注微信公众账号「曹当家的」,订阅最新文章推送