Nginx基础篇05—主配置文件说明

概述

本章,您将学习到主配置文件 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 实际能处理的总连接数上限?

有两种方式:

  1. 修改 /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 内存,还需要预留一部分内存用于缓存或其他服务)

  2. 在 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 的三次握手,其实就是指三次交互过程:

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

笔记
内核参数的配置都可以写入到 **/etc/sysctl.conf** 文件中,修改完成后使用 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 值)中的最小值决定。

Avatar photo

关于 陸風睿

GNU/Linux 从业者、开源爱好者、技术钻研者,撰写文档既是兴趣也是工作内容之一。Q - "281957576";WeChat - "jiulongxiaotianci",Github - https://github.com/jimcat8
用一杯咖啡支持我们,我们的每一篇[文档]都经过实际操作和精心打磨,而不是简单地从网上复制粘贴。期间投入了大量心血,只为能够真正帮助到您。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇