Nginx 配置文件在线生成: https://nginxconfig.io/
Nginx 让人头疼的地方不在于指令难,而在于指令太多,而且不告诉你它属于哪个模块。你想做个限速,查到 limit_rate;想做个健康检查,查到 health_check,兴冲冲配上去 reload,报 unknown directive。原因是它压根不在你编译进来的模块里,或者干脆是商业版才有的。
这篇把 Nginx 里我实际用过的模块按能力分门别类过一遍,每个模块回答三个问题,它能干什么、什么场景下你会需要它、参数里哪几个是真正要调的。清单里的配置片段全部保留原样,我在每段前后补上了它在解决什么问题,以及自己踩过的坑。写于 2018 年,涉及 SSL、HTTP/2 这类会过时的部分,我把当年的写法留着,另起小段说明现在的情况。
在本篇文章中,我们将从浅入深,和大家一起学习以下知识:
- 进程与事件层的性能参数,
worker_processes到底该写几 ngx_http_core_module里那批高频指令,从listen到limit_except- 访问控制、用户认证、状态查看这三个「运维刚需」模块
- 日志、压缩、SSL 三个和线上表现直接相关的模块怎么调
rewrite和referer的常见用法与陷阱- 反向代理模块的缓存体系,
proxy_cache_path那一长串参数分别管什么 upstream的调度算法选型,以及哪些参数只在商业版生效stream模块做四层代理,什么时候它比 HAProxy 更合适- 配套的内核参数调优,以及这批参数在今天还成不成立
先说明一点,下面这些模块里,标准发行版默认编译进来的只是一部分。stub_status、ssl、stream 这几个都需要在 ./configure 阶段显式打开。装完之后敲一句 nginx -V,把输出里的 configure arguments 看一遍,你手上这个二进制有什么能力,那一行说得清清楚楚。这一步能省掉大量「配了没生效」的排查时间。
# 一、性能相关配置
这一组指令都写在配置文件的最外层,也就是 main 上下文,它们决定 Nginx 起几个干活的进程、每个进程能扛多少连接。调错了的表现不是报错,而是压测时怎么都上不去。
worker_processes number | auto;
worker进程的数量;通常应该为当前主机的cpu的物理核心数。多于8个的话建议写8,超过8个性能不会提升,稳定性降低
原文这句「多于 8 个建议写 8」是当年多数机器只有四核八核的经验值,现在服务器动辄三四十核,直接写 auto 让 Nginx 自己读 CPU 核心数就行。有一个场景要留神,在容器里跑的时候 auto 读到的是宿主机的核心数,而不是你给容器限的那点配额。给容器限了 2 核却起了 32 个 worker,进程之间抢 CPU 反而更慢。这种情况下就得按配额写死。
worker_cpu_affinity auto [cpumask] #将work进程绑定在固定cpu上提高缓存命中率
# 例:
worker_cpu_affinity 0001 0010 0100 1000;
worker_cpu_affinity 0101 1010;
worker_cpu_affinity 是把每个 worker 钉死在指定的 CPU 核心上,减少进程在核心之间来回迁移带来的缓存失效。掩码是二进制位,0001 表示第一个核,0010 表示第二个核,一行写几组就是几个 worker 各自绑一个核。这个参数只在 Nginx 是这台机器上的主力进程时才划算,机器上还跑着数据库或者别的服务,绑核反而会让调度器没法腾挪。日常我基本不动它。
worker_priority number
# 指定worker进程的nice值,设定worker进程优先级: [-20,20]
这里原文写的取值区间不准,Linux 的 nice 值范围是 -20 到 19,越小优先级越高。想让 Nginx 在混部机器上抢到更多 CPU,可以往负值调,但改之前先确认这台机器上没有比它更重要的服务。
worker_rlimit_nofile number
worker # 进程所能够打开的文件数量上限,默认较小,生产中需要调大如65535。系统资源通过配置修改/etc/security/limits.conf 例:root soft nofile 65535,或命令修改ulimit -n,修改后需重启服务或系统生效。
这个参数是我认为整组里最容易被忽略、也最容易背锅的一个。Nginx 每接一个连接就要占一个文件描述符,反向代理场景下还要再占一个连回后端的。系统默认的 ulimit -n 通常是 1024,你把 worker_connections 配成 10240 是没用的,连接数到一千出头就开始报 too many open files。
改这个参数得两头一起改,配置里的 worker_rlimit_nofile 和系统的 /etc/security/limits.conf 缺一不可。
# 二、事件驱动events相关的配置
events 块管的是「怎么收连接」。它只有三四个指令,但每一个都直接决定并发上限。
- 每个
worker进程所能够打开的最大并发连接数数量,如10240 - 总最大并发数:
worker_processes * worker_connections
worker_connections number
这里的算法要注意,worker_processes * worker_connections 算出来的是「连接数」不是「并发请求数」。做反向代理的时候,一个客户端请求要占两个连接(客户端来的一个,转发到后端的一个),所以能撑的实际并发请求数要再除以 2。这个换算我一开始也是漏掉的,按 10240 去估容量,实测只有一半。
- 指明并发连接请求的处理方法,默认自动选择最优方法不用调整
use method
# 如:use epoll;
use 这个指令基本不用手动写。Linux 上 Nginx 会自动选 epoll,FreeBSD 上选 kqueue,都是当前平台最优的那个。手写它唯一的用处是排查问题时确认它到底选了谁,nginx -V 和启动日志里都能看到。
on指由各个worker轮流处理新请求Off指每个新请求的到达都会通知(唤醒)所有的worker进程,但只有一个进程可获得连接,造成「惊群」,影响性能,默认on
# 处理新的连接请求的方法
accept_mutex on | off # 互斥;
accept_mutex 是历史遗留问题的解法。早年内核没有解决惊群,多个 worker 同时被唤醒去抢一个连接,抢不到的白白唤醒一次,CPU 就浪费在这上面。加个互斥锁让 worker 轮流去 accept,惊群没了,但代价是新连接进来要等锁,高并发下反而增加延迟。
现在的情况反过来了。较新的 Nginx 版本里 accept_mutex 默认是 off,因为内核层面已经有 EPOLLEXCLUSIVE 和 SO_REUSEPORT 来处理惊群,比应用层加锁高效得多。真正推荐的做法是在 listen 上加 reuseport,让内核直接把连接分发到各个 worker,连锁都不用。原文写的默认值 on 是老版本的行为,具体默认值随版本变过,改之前对着你手上那个版本的官方文档确认一下。
# 三、http核心模块相关配置ngx_http_core_module
ngx_http_core_module 是唯一一个不用编译开关、不用额外声明就一定在的模块。你在 nginx.conf 里写的 server、location、root、listen,全部由它提供。所以它不像别的模块那样有一个明确的「功能边界」,它更像是 Nginx 的语法本身。
这一节的指令数量最多,我按用途分成了几块,实际配置时会反复回来查的也就是下面这十来个。
# 3.1web服务模板
一个虚拟主机的最小骨架就三行,监听哪个端口、认哪个域名、文件在哪。
server { ... }
# 配置一个虚拟主机
server {
listen address[:PORT]|PORT;
server_name SERVER_NAME; # 指令指向不同的主机名
root /PATH/TO/DOCUMENT_ROOT;
}
这个骨架有个容易忽略的点,root 写在 server 层还是 location 层结果不一样。写在 server 层是所有 location 的默认值,写在某个 location 里就只对那一段生效并且覆盖外层。混着写的时候,找不到文件的 404 往往就出在这儿。
# 3.2套接字相关配置
listen 决定这个 server 块从哪个地址、哪个端口收请求,后面那一串可选参数平时用到的不多,但每一个都是在特定场景下救过命的。
listen address[:port] [default_server] [ssl] [http2 | spdy] [backlog=number] [rcvbuf=size] [sndbuf=size]
default_server设定为默认虚拟主机ssl限制仅能够通过ssl连接提供服务backlog=number超过并发连接数后,新请求进入后援队列的长度rcvbuf=size接收缓冲区大小sndbuf=size发送缓冲区大小
default_server 管的是「域名对不上时给谁」。有人拿 IP 直接访问你的服务器,或者把自己的域名解析到你的 IP 上,这类请求匹配不到任何 server_name,Nginx 就交给 default_server。不显式指定的话,同端口第一个 server 块自动当默认。生产环境我一般会专门写一个只返回 444 的默认块,把这类野请求直接掐掉。
backlog 是内核层面的等待队列长度,短时间涌进来大量新连接、worker 一时来不及 accept 的时候,多出来的连接排在这个队列里。队列满了内核就直接丢,客户端表现为连接超时而不是 502。这个值同时受 net.core.somaxconn 限制,只调 Nginx 不调内核是没用的,这一点和后面那份 sysctl 调优是配套的。
spdy 现在已经不用看了,它是 HTTP/2 标准化之前的过渡协议,早就从 Nginx 里移除。至于 listen 443 ssl http2 这个写法,新版本改成了独立的 http2 on; 指令,老写法一段时间内还兼容但会有 deprecated 提醒。你手上是哪种,以 nginx -V 的版本号对着官方文档看,别照抄博客。
# 3.3 server_name
- 支持*通配任意长度的任意字符
server_name *.magedu.com www.magedu.*
- 支持
~起始的字符做正则表达式模式匹配,性能原因慎用
server_name ~^www\d+\.magedu\.com$ #\d 表示 [0-9]
匹配优先级机制从高到低:
- 首先是字符串精确匹配 如:
www.magedu.com - 左侧
*通配符 如:*.magedu.com - 右侧
*通配符 如:www.magedu.* - 正则表达式 如:
~^.*\.magedu\.com$ default_server