概述
本章,您将学习到主配置文件 nginx.conf 的相关知识。
主配置文件中的概念与规定
-
主配置文件中的配置项被称为 指令,用来控制 Nginx 的全局行为
-
指令 分为 简单指令(Simple Directives) 与 块指令(Block Directives)
-
简单指令 - 由指令名称和指令参数组成,两者使用空格分隔并用分号(
;)结尾,比如worker_processes 1;。如果指令参数中包含了空格、分号、大括号等特殊字符,必须用单引号或双引号将指令参数包裹,避免语法解析错误 -
块指令 - 由指令名称与一对花括号(左花括号
{和右花括号})构成,比如:events { work_connections 1024; }若块指令包含了其他指令,则可以被称为 上下文(Context),比如上面的示例指的是 events 上下文。需要注意的是,某些简单指令只能配置在特定的上下文中
-
不在任何花括号内的指令,属于 main 上下文(main context) 这个层级,在这个层级中的简单指令可影响 Nginx 的全局行为
-
不同上下文之间可以相互嵌套,比如:
server { location ~ \.(gif|jpg|png)$ { root /data/images; } }常见的嵌套关系是 http 上下文包裹 server 上下文,server 上下文包裹 location 上下文。
-
"#" 符号开头到行尾的内容都属于注释,比如:
# 这是一行注释 worker_processes 1; # 这行后面的也是注释 -
使用
include指令包含其他目录中的.conf文件 -
在书写文档时,以下的文字表述都正确:
- 从层级范围表述 - 将这个简单指令配置在 events 上下文中(Nginx 官方更常使用 "上下文" 一词)
- 从语法角度表述 - 将这个简单指令配置在 events 指令块中
指令说明
仅说明 nginx.conf 中已存在的默认指令,其他指令则会在演示 Nginx 的具体功能时进行说明。相关指令的语法与说明可以参考 官方文档。
Shell > cat /usr/local/nginx/conf/nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
...
}
...
}
-
user指令 - 定义 worker 进程应该使用哪个用户和用户组来运行,语法为user user [group];。建议修改为 nginx 用户,即user nginx;。仅能配置在 main 上下文中 -
worker_processes指令 - 定义 worker 进程的数量,指令参数默认为 1。根据服务器的整体负载情况,其指令参数通常设置为可用的 CPU 核心数,若不能确定,可将指令参数设置为 "auto" 自动检测该数值。仅能配置在 main 上下文中 -
error_log- 指定错误日志文件以及日志级别,语法为error_log file [level] [json];,默认为error_log logs/error.log error;。可配置在 main、http、mail、stream、server、location 上下文中 -
pid指令 - 定义进程 ID 文件,语法为pid file;,默认为pid logs/nginx.pid;。仅能配置在 main 上下文中 -
events上下文 - 为连接处理提供配置指令,语法为events { ... }。仅能配置在 main 上下文中worker_connections指令 - 单个 worker 进程处理的连接数(可打开的最大文件数)上限。Nginx 最大能处理的连接数上限取决于三方面,在本文档的后面会专门介绍。仅能配置在 events 上下文中
-
http上下文 - 为 HTTP 服务器提供配置指令,语法为http { ... }。仅能配置在 main 上下文中include指令 - 使用该指令包含其他配置文件,这里是将 MIME 类型映射文件包含进来default_type指令 - 定义响应的默认 MIME 类型log_format指令 - 配置日志的格式并给这个格式定义一个名称,在示例中,main 就是格式的名称,后续可以调用access_log指令 -定义访问日志文件的路径以及所使用的格式名称,默认为access_log logs/access.log combined;。可配置在 http、server、location、if in location、limit_except 上下文中sendfile指令 - 是否调用系统级 sendfile() 函数来开启高效文件传输模式。可配置在 http、server、location、if in location 上下文中tcp_nopush指令- 在 GNU/Linux 中,是否启用或禁用 TCP_CORK 套接字选项keepalive_timeout指令 - 定义长连接(持久连接)的超时时间。若指令参数不指定时间单位,则默认使用秒(s)gzip指令 - 是否开启 Gzip 压缩响应功能,即告诉 Nginx 服务器在发送响应数据给客户端(如浏览器)之前,先对数据进行 Gzip 算法压缩
-
server上下文 - 用来给虚拟主机进行配置,语法为server { ... }。5 个同名的 server 上下文由不同的模块进行提供,在示例中, server 上下文由 ngx_http_core_module 模块提供且只能存在于 http 上下文中。listen指令 - 设置服务器用于接收请求的 IP 地址和端口,默认为listen *:80。3 个同名的 listen 指令由不同的模块进行提供。对于 IPv4 ,示例用法listen 127.0.0.1:8000;、listen 127.0.0.1;、listen 8000;、listen *:8000;、listen localhost:8000;都能被接受。对于 IPv6,示例用法listen [::]:8000;、listen [::1];都能被接受。您也可以在指令参数中指定 UNIX 域套接字,比如listen unix:/var/run/nginx.sock;。在上面的示例中,该指令只能存在于 server 上下文中server_name指令 - 设置虚拟主机的名称。3 个同名的 server_name 指令由不同的模块进行提供。在上面的示例中,该指令只能存在于 server 上下文中error_page指令 - 定义在出现指定错误时将显示的 URI(统一资源标识),语法为error_page code ... [=[response]] uri;
-
location上下文 - 根据请求的 URI(统一资源标识)进行设置,只能存在于 server 上下文和 location 上下文中。root指令 - 设置请求的根目录,语法为root path;。路径可以是绝对路径,也可以是相对路径(示例就使用相对路径下的 html 目录)。index指令 - 设置网页的一个或多个索引文件,语法为index file ...;。该指令只能存在于 http、server、location 上下文中。
在默认的 nginx.conf 文件中,层级关系为:
- main 上下文包裹 http 上下文
- http 上下文包裹 server 上下文
- server 上下文包裹 location 上下文
Nginx 基础性能调优
Nginx 处理的总连接数
前面的 Redis 7 教程中我们介绍过,在 GUN/Linux 中,引入了一个被称为「文件描述符」的东西,即内核为了高效管理对已被打开的文件创建的索引,用于指向被打开的文件,所有 IO 操作的系统调用都通过文件描述符。文件描述符是一个非负整数,用于标明每一个被进程所打开的文件。在 Windows 系统中,这个东西被称为句柄。
GNU/Linux 是多任务多用户的,当多个用户同时打开同一个文件时,如何管理文件的偏移量就成为了一个问题(因为每个用户所执行的操作都不是一样的),所有引入了一个叫做 文件描述符(File descriptor) 的东西来为每一个用户服务。每个用户每次打开一个文件,就产生一个文件描述符,多次打开就产生多个文件描述符,一 一对应。
文件描述符有用户级别和系统级别:
- 系统级别 -
cat /proc/sys/fs/file-max,这里限制的是所有用户打开文件描述符的总上限。其值由系统自动计算,通常为内存(以 KB 为单位)的 10% 左右 - 用户级别 - 通过修改
/etc/security/limits.conf来限制。默认情况下,单个用户的文件描述符为 1024
# 查看系统级别的文件描述符总上限
Shell > cat /proc/sys/fs/file-max
366421
# 查看当前登录用户可使用文件描述符的上限。
Shell > ulimit -n
1024
Nginx 能处理的最大连接数取决于三方面:系统级别的文件描述符上限、单个用户可使用的文件描述符上限、主配置文件中的总连接数。假设 worker_processes 指令的指令参数设置为 4 ,但 worker_connections 指令的指令参数设置为 1024,站在 Nginx 角度来看,可处理的总连接数(可打开的文件数)为 4096,但由于存在用户级别的文件描述符上限,因此 Nginx 实际能处理的最大连接数为 1024,这对于一个 Web 服务器来说远远不够,因此我们需要进行调整。另外要说明的是,Nginx 能处理的最大连接数并不是设置为越大越好,需要根据实际业务情况与内存占用情况(Nginx 中,单个连接大约占用 10~50KB 或更大的内存)来综合评估最后需要设置多少。
Q:如何更改 Nginx 实际能处理的总连接数上限?
有两种方式:
-
修改 /etc/security/limits.conf 文件的内容,假设您 Nginx 程序的 worker 进程使用 nginx 用户来运行,在该文件中追加两行:
nginx soft nofile 30000 nginx hard nofile 30000假设您机器的 CPU 为 4 核且拥有 4GB 的内存,可将
worker_processes指令的指令参数设置为 4 ,worker_connections指令的指令参数设置为 7400 。在 Nginx 满负载的情况下,所有 worker 进程大约占用 1GB 的内存,资源分配是合理的(在 Rocky Linux 8.x 的纯命令行交互中,操作系统的系统组件以及服务可能占用 1-1.5GB 内存,还需要预留一部分内存用于缓存或其他服务) -
在 nginx.conf 的 main 上下文中使用
worker_rlimit_nofile指令,该指令用来设置每个 worker 进程能够打开的最大文件描述符(File Descriptor, FD)数量,可主动向操作系统申请更多文件描述符资源。假设您机器的 CPU 为 4 核且拥有 4GB 的内存,可将worker_processes指令的指令参数设置为 4 ,worker_connections指令的指令参数默认为 1024,而将worker_rlimit_nofile指令的指令参数设置为 7400
指定连接处理方法
Nginx 支持多种连接处理的方法,在 GNU/Linux 操作系统,推荐的连接处理方法是 epoll,您可以在配置文件中显式配置:
...
events {
use epoll;
...
}
...
use 指令只能存在于 events 上下文中。
单个 worker 进程对待处理的连接进行批处理
multi_accept 指令的指令参数为:
-
off(默认)- 当 epoll 被通知有新的连接就绪时,worker 进程会调用一次 accept() 获取一个连接,然后立即返回去处理其他事件(如读写数据)。如果此时内核队列中有 100 个新连接,worker 进程需要被唤醒 100 次才能全部接收。
-
on - 当 epoll 被通知有新的连接就绪时,worker 进程会在一个循环中连续调用 accept(),直到内核监听队列为空。这样,worker 进程只需一次唤醒即可处理多个新连接。
...
events {
use epoll;
multi_accept on;
...
}
...
调整 TCP backlog 全连接队列大小
关于 TCP backlog 的知识,在前面 Redis 配置文件 中说明过。
TCP backlog 是一个处理关于 TCP 连接队列的这样一个参数(与三次握手相关),在高并发场景下,参数值的多少与性能直接挂钩。
众所周知,在 TCP/IP 协议族当中,在传输层这里有两个协议:
- TCP(传输控制协议):面向连接的协议,即收发数据前,必须与对方建立可靠的连接。拥有三次握手、四次挥手的特征。相比 UDP,TCP能够保证数据的准确性,同时还更加安全。
- UDP(用户数据报协议):一种无连接的传输协议,提供面向事务的不可靠信息传输服务,其传输的速度相比 TCP 更快,但数据有可能丢失。
首先回顾下 TCP 的报头结构:

标志:数据标志,6个标志只能选择其中的一个。
- URG - 紧急指针字段,当 URG=1 时,此字段告诉系统此报文段中有紧急数据,应尽快传送
- ACK - 确认字段,当 ACK=1 时,表示确认,且确认号有效;当ACK=0时,确认号字段无效
- PSH - 推送字段,当 PSH=1 时,提示接收端应用程序应该立即从TCP接受缓冲区中读走数据,为接受后续数据腾出空间。如果不将接收到的数据读走,它们就会一直停留在 TCP 报文段
- RST - 复位字段,当 RST 为 1 时,表明 TCP 连接中出现了严重的差错,必须释放连接,然后再重新建立连接。我们称携带 RST 标志的 TCP 报文段为复位报文段。
- SYN - 同步字段,当 SYN=1 时,表示发起一个连接请求。我们称携带 SYN 标志位的 TCP 报文段为同步报文段。
- FIN - 中止字段,用来释放连接。当 FIN=1 时,表明此报文段的发送端的数据已发送完成,并要求释放连接。我们称携带 FIN 标志的 TCP 报文段为结束报文段。
所谓 TCP 的三次握手,其实就是指三次交互过程:

状态说明:
- CLOSED - 没有任何连接状态
- LISTEN - 监听来自客户端的连接请求
- SYN_SENT - 同步发送状态
- SYN_RCVD - 同步接收状态
- ESTAB_LISHED - 打开的连接,数据可以传输。
三次握手的过程就好像打电话:
- A:「服务器B,你在吗?」
- B:「我在。」
- A:「我要给你传输数据。」
当服务端调用 listen() 等函数时,状态由 CLOSED 变更为 LISTEN,此时的 Linux 内核会 创建/维护 两个队列:
- 半连接队列(Incomplete Connection queue,又称 SYN 队列) - 当服务端接收到了客户端的 SYN 且返回了 SYN 和 ACK,此时服务端状态变更为 SYN_RCVD ,会将用户的请求放入到半连接队列中。
- 全连接队列(Completed Connection queue,又称 ACCEPT 队列) - 等到客户端返回 ACK,此时三次握手完成,半连接队列会被释放且移动到全连接队列。
当半连接队列或全连接队列发生了问题(比如队列满了),TCP 三次握手的机制就不成功,因此在一些高并发的场景下需要调整队列的配置。
# 在 Linux 内核当中,半连接队列的大小取决于这个文件的内容——/proc/sys/net/ipv4/tcp_max_syn_backlog
## 在 Rocky Linux 8.x 中,这个值为 128
Shell > cat /proc/sys/net/ipv4/tcp_max_syn_backlog
128
# 全连接队列的大小由两部分决定——listen 函数里的 backlog 以及 Linux 内核参数 /proc/sys/net/core/somaxconn,最终的全连接队列大小由数字最小的那个来决定
Shell > cat /proc/sys/net/core/somaxconn
2048
Q:如何知道某个服务的全连接队列大小?
使用 ss -tulnp 命令查阅 Send-Q 这一列。
Q:如果全连接队列满了,Linux 内核会如何处理?
与这个内核参数有关——/proc/sys/net/ipv4/tcp_abort_on_overflow ,有效值为 0 或 1
- 0 值:重试。在客户端等待超时后,重新发送 ACK,让服务端重新进入到 ESTAB_LISHED 状态
- 1 值:断开连接。服务端会发送 RST 包给客户端,客户端会收到诸如 "104 Connection reset by peer" 的错误
Shell > cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0
sysctl -p 命令重新加载,使修改的内容生效。首先调整 Linux 内核的半连接队列大小和全连接队列大小:
Shell > vim /etc/sysctl.conf
net.core.somaxconn = 8000
net.ipv4.tcp_max_syn_backlog = 8000
net.ipv4.tcp_abort_on_overflow = 1
Shell > sysctl -p
接着在 Nginx 的主配置文件中调整全连接队列大小:
...
http {
server {
listen *:80 backlog=8000 reuseport;
}
}
...
配置完成后,Nginx 程序启动成功后会调用 listen 函数并传入 backlog 的值。同样的,Nginx 程序最终的全连接队列大小由两者(Linux 内核参数值和传入 listen 函数的 backlog 值)中的最小值决定。










