Nginx 采用 master-worker 进程模式,worker 进程数需与CPU核数量相同。多进程之间使用共享内存,父子进程之间是使用信号管理的。
1 | ps -ef | grep nginx |
Master 进程
- 监控 worker 进程: chld
- 接收信号,管理 worker 进程
- TERM, INT 立刻停止
- QUIT 优雅的停止
- HUP 重载配置文件
- USR1 重新打开日志文件,做日志文件切割
- USR2 使用 kill 命令管理,用于升级 nginx。
- WINCH 使用 kill 命令管理,用于升级 nginx。
Worker 进程
- TERM, INT
- QUIT
- USR1
- WINCH
Nginx 命令行
- reload: HUP
- reopen: USR1
- stop: TERM
- quit: QUIT
正则表达式效验工具 pcretest
CentOS 8
1
2
3
4
5
6
7$ yum install -y pcre2-tools
$ pcre2test
PCRE2 version 10.32 2018-09-10
re> /article\/detail\?id=(\d+)/
data> article/detail?id=123
0: article/detail?id=123
1: 123
平滑升级 nginx
1 | mv nginx nginx.old |
日志切割
1 | mv logs/access.log back.log |
GoAccess
可视化实时监控 access 日志。
HTTPS 部署
1 | yum install python2-certbot-nginx |
握手缓存:session
支持的安全套件:
Openresty
待处理
gzip
error_page 404 =200 /404.html
error_page 500 502 503 504 /50x.html
return 200 “Hello world”
rewrite
在
server
块中可通过配置rewrite_log on
参数,将rewrite
请求记录到 nginxaccess
日志中。if
- 判断文件是否存在,-f 与 !-f
- 判断目录是否存在, -d 与 !-d
- 判断文件、目录、软连接是否存在, -e、!-e
- 值是否相等,=、!=
- 正则表达式
、!- 忽略大小写:
~*
、!~*
location [=|
|*|^~] uri {…}Syntax: location [=|
|*|^~] uri {…}Context: server, location
合并连续的
/
符号,Default: merg_slashes on匹配规则
- 前缀字符串
- 常规匹配,示例:location /test {…}
- =: 精准匹配,示例:location = /test {…}
- ^~: 匹配后则不再进行正则表达式匹配
- 正则表达式
- ~: 大小写敏感的正则表达式
- ~*: 忽略大小写的正则表达式
- 用于内部跳转的命名 location
- @
- 前缀字符串
匹配顺序
无顺序匹配全部前缀字符串 location
使用匹配上的 = 精确匹配 location:
匹配 = 字符串
。使用匹配上的 ^~ 字符串 location:
匹配 ^~ 字符串
。多个匹配项,记住最长匹配的前缀字符串 location
按照 nginx.conf 中的顺序依次匹配正则表达式 location。
- 匹配成功,使用匹配上的正则表达式。
- 匹配失败,所有表达式都不匹配,使用最长匹配的前缀字符串location。
auth_basic
auth_basic “test auth_basic”
auth_basic_user_file file
生成工具:
htpasswd
,可通过执行yum install -y httpd-tools
命令安装。1
htpasswd -c -b file user pass
try_files
默认安装、无法禁用
- Syntax
- try_files file … uri;
- try_files file … =code;
- Context
- server
- location
- Syntax
mirror
实时拷贝流量:precontent 阶段的 mirror 模块,默认编译进 Nginx,编译时可通过
--without-http_mirror_module
参数移除。处理请求时,生成子请求访问其他服务,对子请求的返回值不做处理。
Syntax: mirror uri | off;
Default: mirror off;
Context: http, server, location
Syntax: mirror_request_body on | off;
Default: mirror_request_body on;
Context: http, server, location
content阶段
static: root 和 alias 指令
- alias
- Context: location
- root
- Default: root html;
- Context: http, server, location, if in location
功能:将 url 映射为文件路径,以返回静态文件内容
区别:
- root 会将完整 url 映射到文件路径中。
- alias 只会将 location 后的 URL 映射到文件路径。
- alias
关闭未找到文件时的错误日志:log_not_found off;
展示目录:autoindex on;
将多个小文件合并到一个http请求,最终将所有文件内容合并进行响应:
ngx_http_concat_module
。access_log path 可使用变量,但会造成频繁开关文件。可通过
open_log_file_cache
参数进行优化。变量
- Nginx 内置变量
- arg_参数名
- args 全部参数
- http 变量
- tcp 变量
- Nginx 内置变量
防盗链 valid_referers none blocked server_names *.codezm.com
$invalid_referer
keepalive
提升链接效率1
2
3
4
5
6
7
8
9
10# nginx 向客户端返回的 keepalive
# 禁用 IE6 浏览器使用 keepalive 功能。
keepalive_disable msie6;
# 在同一个 Tcp 链接上最多处理几个 http 请求。
keepalive_requests 100;
# Tcp 链接处理后最多保持 75s 后关闭链接,nginx 告知客户端最多 25s 后关闭链接。
keepalive_timeout 75 25;
proxy_http_version 1.1;
proxy_set_header Connection "keepalive";
- 在
http
块中增加server_tokens off;
关闭Response Headers
中Server
对应nginx
版本号,以提升安全性。
四、反向代理与负载均衡
7层应用反向代理:
uwsgi
fastcgi
scgi
http
memcached
websocket http://www.websocket.org/echo.html
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
stream 4层反向代理:
upstream 模块提供的变量
名称 | 释义 |
---|---|
$upstream_addr | 上游服务器的IP地址,格式为可读的字符串,例如:127.0.0.1:8080 |
$upstream_connect_time | 与上游服务建立连接消耗的时间,单位为秒,精确到毫秒 |
$upstream_header_time | 接收上游服务发回响应中http头部所消耗的时间,单位为秒,精确到毫秒 |
$upstream_response_time | 接收完整的上游服务响应所消耗的时间,单位为秒,精确到毫秒 |
$upstream_http_名称 | 从上游服务返回的响应头部的值 |
$upstream_bytes_received | 从上游服务接收到的响应长度,单位为字节 |
$upstream_response_length | 从上游服务返回的响应包体长度,单位为字节 |
$upstream_status | 上游服务返回的HTTP响应中的状态码,如果未连接上,该变量值为502 |
$upstream_cookie_名称 | 从上游服务返回发回的 Set-Cookie 中取出的 cookie 值 |
$upstream_trailer_名称 | 从上游服务的响应尾部取到的值 |
$upstream_http_server | 上游服务 |
$upstream_cache_status | 缓存状态 |
最大包体长度限制
client_max_body_size 1m;
仅对请求头部中含有 Content-Length
有效超出最大长度后,返回 413
错误。
临时文件路径格式
client_body_temp_path path [level1 [level2 [level3]]]
,默认 client_body_temp_path client_body_temp;
。
client_body_in_file_only on|clean|off
,默认 client_body_in_file_only off;
。
读取包体时的超时
client_body_timeout time;
,默认 client_body_timeout 60s;
。读取包体时超时,则返回 408 错误。
向上游服务建立连接
Syntax: proxy_connect_timeout time;
Default: proxy_connect_timeout 60s;
Context: http,server,location
超时后,会向客户端生成 http 响应,响应码为:502。
Syntax: proxy_next_upstream http_502|…;
Default: proxy_next_upstream error timeout;
Context: http,server,location
当上游连接返回 502
错误码后,可以再设置另一个上游服务提供处理。
上游连接启用 TCP keepalive
Syntax: proxy_socket_keepalive on|off;
Default: proxy_socket_keepalive off;
Context: http,server,location
上游连接启用 HTTP keepalive
Syntax: keepalive connections;
Default: -;
Context: upstream
一个 tcp 连接最多处理多少个http请求。
Syntax: keepalive_requests number;
Default: keepalive_requests 100;
Context: upstream
向上游发送 HTTP 请求
Syntax: proxy_send_timeout time;
Default: proxy_send_timeout 60s;
Context: http,server,location
接收上游的 HTTP 响应头部
Syntax: proxy_buffer_size size;
Default: proxy_buffer_size 4k|8k;
Context: http,server,location
error_log: upstream sent too big header
接收上游的 HTTP 包体
Syntax: proxy_buffer number size;
Default: proxy_buffer 8 4k|8k;
Context: http,server,location
Syntax: proxy_buffering on|off;
Default: proxy_buffering on;
Context: http,server,location
使用上游服务设置的 Server
header 头配置。
proxy_pass_header server;
用 error_page 拦截上游失败响应
当上游响应的响应码 >= 300 时,应将响应返回客户端还是按 error_page 指令处理
error_page 500 /500.html;
proxy_intercept_errors on;
缓存
proxy_cache_path
LRU淘汰算法:末位淘汰。
查看上下文切换次数
- vmstat 1
- dstat 1
- pidstat -w 1 -p
惊群
reuseport
linux > 3.9,centos >= 7.0。listen 80 reuseport;
提升 cpu 缓存命中率
work_cpu_affinity: auto [cpumask];
1 | # cpu L1级指令缓存 |
TCP
netstat -nap | grep tcp
SYN_SENT状态
主动建立连接时,发 SYN 的重试次数:net.ipv4.tcp_syn_retries = 6
。
建立连接时的本地端口可用范围:net.ipv4.ip_local_port_range = 32768 60999
。
主动建立连接时,应用层超时时间:proxy_connect_timeout 60s;
。
SYN_RCVD 状态 -服务端
(半链接)SYN Queue
SYN_RCVD 状态链接的最大个数:net.ipv4.tcp_max_syn_backlog
被动建立连接时,发 SYN/ACK 的重试次数:net.ipv4.tcp_synack_retries
。
(全连接)ACCEPT Queue
最大个数:backlog
如何应对 SYN 攻击
- 接收自网卡、但未被内核协议栈处理的报文队列长度:
net.core.netdev_max_backlog
。 - SYN_RCVD 状态连接的最大个数:
net.ipv4.tcp_max_syn_backlog
。 - 超出处理能力时,对新来的SYN直接回包RST,丢弃连接:
net.ipv4.tcp_abort_on_overflow
。 - 启用 cookie:
net.ipv4.tcp_syncookies=1
。
句柄数的上限
操作系统全局
- 操作系统可使用的最大句柄数:
fs.file-max
,sysctl -a | grep file-max
。 - 使用
fs.file-nr
可以查看当前已分配、正使用、上限:fs.file-nr=21632 0 40000500
,sysctl -a | grep file-nr
- 操作系统可使用的最大句柄数:
限制用户
- /etc/security/limits.conf
- root soft nofile 65535
- root hard nofile 65535
- /etc/security/limits.conf
限制进程
worker_rlimit_nofile number;
Nginx 上游+下游间连接句柄数限制: worker_connections number;
两个队列的长度
SYN队列 - 未完成握手:net.ipv4.tcp_max_syn_backlog = 262144
。
ACCEPT 队列 - 已完成握手:net.core.somaxconn
,系统级最大 backlog
队列长度。
Nginx: listen address[:port] [backlog=number]
。限制已握手队列。
Tcp fast open
net.ipv4.tcp_fastopen
- 0:关闭(默认)
- 1:作为客户端时可以使用TFO
- 2:作为服务端时可以使用TFO
- 3:无论作为客户端还是服务端,均使用TFO
proxy_timeout: 1m;
TCP缓冲区
当读、写超时指令生效引发连接关闭时,通过发送 RST
立刻释放端口、内存等资源来关闭链接: reset_timeout_connection on|off;默认:
off`。
TLS/SSL 优化握手性能
ssl_session_cache off | none
- off
- 不使用 Session 缓存,且 Nginx 在协议中明确告诉客户端 Session 缓存不被使用
- none
- 不使用 Session 缓存
- builtin
- 使用 Openssl 的 Session 缓存,由于在内存中使用,所以仅当同一客户端的两次链接都命中到同一个 worker 进程时,Session 缓存才会生效。
- shared:name:size (常用)
- 定义共享内存,为所有 worker 进程提供 Session 缓存服务。1MB 大约可用于 4000 个 Session。
1 | # 1m 大约提供 4000 个链接 |
https://ssl-config.mozilla.org/
TLS/SSL 中的会话票证 tickets
Nginx 将会话 Session 中的信息作为 tickets 加密发给客户端,当客户端下次发起 TLS 链接时带上 tickets,由 nginx 解密验证后复用会话 Session。
会话票证虽然更易在 Nginx 集群中使用,但破坏了 TLS/SSL 的安全机制,有安全风险,必须频繁更换 tickets 密钥。
是否开启会话票证服务:
Syntax: ssl_session_tickets on | off;
Default: ssl_session_tickets on;
Context: http,server
使用会话票证时加密 tickets 的密钥文件,未配置时会随机生成:
Syntax: ssl_session_ticket_key file;
Default: -
Context: http,server
1 | ssl_session_ticket_key ticket.key; |
1 | openssl rand 80> ticket.key |
HTTP 长连接
优点
- 减少握手次数
- 通过减少并发连接数减少了服务器资源的消耗
- 降低 TCP 拥塞控制的影响
Syntax: keepalive_requests number;
Default: keepalive_requests 100;
Context: http,server,location
Syntax: keepalive_requests number;
Default: keepalive_requests 100;
Context: upstream
gzip 压缩 - http://nginx.org/en/docs/http/ngx_http_gzip_module.html
功能:
通过实时压缩 http 包体,提升网络传输效率
模块:ngx_http_gzip_module
,通过 --without-http_gzip_module
禁用模块
Syntax: gzip on | off;
Default: gzip off;
Context: http,server,location, if in location
1 | gzip on; |
指定压缩文件类型
Syntax: gzip_types mime-type …;
Default: gzip_types text/html;
Context: http,server,location
1 | gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml; |
指定压缩文件的最小字节数,当小于此字节数时不进行压缩。
Syntax: gzip_min_length length;
Default: gzip_min_length 20;
Context: http, server, location
1 | gzip_min_length 1k; |
通过设置正则规则,表明哪些 User-Agent
头不使用 gzip 压缩。
Syntax: gzip_disable regex …;
Default: —
Context: http, server, location
1 | # 禁用IE 1-6 gzip |
指定压缩的 http 版本,默认:http_version: 1.1 才启用压缩处理。
Syntax: gzip_http_version 1.0 | 1.1;
Default: gzip_http_version 1.1;
Context: http, server, location
是否压缩上游的响应
Syntax: gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any …;
Default: gzip_proxied off;
Context: http, server, location
- off
- 不压缩来自上游的响应
- expired
- 如果上游响应中含有 Expires 头部,且其值中的时间与系统时间比较后确定不会缓存,则压缩响应
- no-cache
- 如果上游响应中含有
Cache-Control
头部,且其值含有no-cache
值,则压缩响应。
- 如果上游响应中含有
- private
- 如果上游响应中含有
Cache-Control
头部,且其值含有private
值,则压缩响应
- 如果上游响应中含有
- no_last_modified
- 如果上游响应中没有
Last-Modified
头部,则压缩响应
- 如果上游响应中没有
- no_etag
- 如果上游响应中没有
ETag
头部,则压缩响应
- 如果上游响应中没有
- auth
- 如果客户端请求中含有
Authorization
头部,则压缩响应
- 如果客户端请求中含有
- any
- 压缩所有来自上游的响应
配置缓冲区大小
Syntax: gzip_buffers number size;
Default: gzip_buffers 32 4k|16 8k;
Context: http, server, location
压缩率等级,1-9,1:压缩速度最快,压缩率最低。
Syntax: gzip_comp_level level;
Default: gzip_comp_level 1;
Context: http, server, location
是否在响应中返回 Vary: Accept-Encoding
。
Syntax: gzip_vary on | off;
Default: gzip_vary off;
Context: http, server, location
变量
- 压缩率
$gzip_ratio
1 | # 开启缓存 |
升级更高效的 http2 协议:向前兼容 http/1.x
协议
nginx 开启 HTTP/2 前置条件 - http://nginx.org/en/docs/http/ngx_http_v2_module.html
HTTP/2
是建立在TLS/SSL
协议上的,所以必须有证书支持。nginx >= 1.9.5。可通过
nginx -v
查看当前版本。编译 nginx 时所使用的 openssl >= 1.0.2。可通过
openssl version
查看当前系统openssl
版本,通过nginx -v
可查看编译 nginx 时openssl
版本。
开启 HTTP/2
1 | server { |
检查开启状态
- Chrome 扩展程序:
HTTP/2 and SPDY indicator
。
磁盘IO优化
适用于大文件:直接IO。- http://nginx.org/en/docs/http/ngx_http_core_module.html#directio
Syntax: directio size | off;
Default: directio off;
Context: http, server, location1
2# 当文件 size 大于 10M 时,启用 directio。
directio 10m;当启用
directio
时,将自动禁用sendfile
。异步IO - http://nginx.org/en/docs/http/ngx_http_core_module.html#aio
Syntax: aio on | off | threads[=pool];
Default: aio off;
Context: http, server, location
sendfile on;
将使用零拷贝技术:通过获取对应文件的句柄发送给 socket 缓冲区。
gzip_static 模块 - http://nginx.org/en/docs/http/ngx_http_gzip_static_module.html
模块:ngx_http_gzip_static_module
,通过 --with-http_gzip_static_module
启用模块。
功能:检测到同名 .gz
文件时,返回 .gz
文件内容。
Syntax: gzip_static on | off | always;
Default: gzip_static off;
Context: http, server, location
nginx 性能分析
Google PerfTools https://github.com/gperftools/gperftools
stub_status http://nginx.org/en/docs/http/ngx_http_stub_status_module.html
lua 防火墙 https://github.com/loveshell/ngx_lua_waf
1 | tcpdump -i lo port 514 -A -s 0 |
- 负载均衡(Server Load Balancer,下文简称 SLB)
1 | ./configure --user=apache --group=apache --prefix=/data/nginx-debug \ |
X-Forward_For
HTTP请求11个阶段:
postread
阶段
在 X-Forward-For 的最左边才是真实用户的地址,右边都是代理地址,当我们把 real_ip_header 设置为 X-Forward-For 时,如果没有启用 环回地址 real_ip_recursive,我们取到的 $remote_addr 是 X-Forward-For 中最右边的 ip,无论是否设置了 set_real_ip_from;如果启用了环回地址 real_ip_recursive on,那么就获取最左边的 ip,也就是排除设置了 set_real_ip_from(可信地址外的)。不一定是最左边的IP,而是从右边开始数,第一个非可信地址的IP(set_real_ip_from 可信地址外的)
当 nginx 前存在 SLB、反向代理时,我们是无法直接通过 $remote_addr
得到客户端IP地址的,此时变量 $remote_addr
的值是 SLB
等服务器的IP,并非真实客户端请求IP。
进而无法通过 $remote_addr
变量来对客户端做限流、限速等策略。
可通过 --with-http_realip_module
模块来获取真实客户端IP,默认是没有编译到 nginx
中的,需要通过 ./configure
配置时手动添加,来启用此模块。
测试
1 | curl -H 'X-Forwarded-For: 93.184.216.34, 192.168.96.3, 127.0.0.1' -H 'cdn-src-ip: 122.4.117.22' realip.codezm.com |
限流
限制并发连接。
限速
时间单位
- ms milliseconds
- s seconds
- m minutes
- h hours
- d days
- w weeks
- M months, 30 days
- y years, 365 days
Nginx 内置变量
在 nginx 中,如何使用 Get 参数变量
1 | location / { |
1 | curl "var.codezm.com?name=codezm&age=22" |
在 nginx 中,如何使用自定义 header 变量
1 | location / { |
1 | curl -H 'cdn-src-ip: 93.184.216.34' realip.codezm.com |