概述
- 我们了解下配置文件中的一个全局段,有哪些配置参数,包括后面的 events 字段,有哪些配置参数
- 这里面也有一些核心参数, 对于我们Nginx运行的性能也是有很重要的帮助
- 我们现在首先关注整个 main 段的一个核心参数用法
- 所谓 main 段,是指在 nginx.conf 配置文件中,除了有 events 字段,还有 http 段
- 在此两个段之外的一些段,我们都把它称为 main 段,也就是我们的核心段, 如下
- 前面的这些配置信息,并没有包含在某一个段中,它直接写在我们整个配置文件中
- 我们把这样一些参数就称之为 main 段,也就是全局配置的配置,main 端对于整个 nginx 都是生效的
- 它们通常是来定义 Nginx 整个运行的一些相关参数
- 比如, 需要起几个 worker processes
- 起几个 worker 子进程,
- 每一个 worker 子进程应该工作在哪一种模式下
- 包括我们如何对我们的work子进程和我们的CPU核心进行一个绑定等等
核心参数
1 )全局段核心参数
- user
- user USERNAME [GROUP]
- 解释:指定运行nginx的worker子进程的属主和属组,其中属组可以不指定
- 示例:
user nginx nginx;
- pid
- pid DIR
- 解释:指定运行nginx的master主进程的pid文件存放路径
- 示例:
pid /opt/nginx/logs/nginx.pid;
- worker_rlimit_nofile
- worker_rlimit_nofile number
- 解释:指定worker子进程可以打开的最大文件句柄数
- 示例:
worker rlimit nofile 20480;
- worker_rlimit_core
- worker_rlimit_core size
- 解释:指定worker子进程异常终止后的core文件,用于记录分析问题
- 示例:
worker rlimit core 50M;
- 示例:
working_directory /opt/nginx/tmp;
- worker_processes
- worker processes number | auto
- 解释:指定nginx启动的worker子进程数量
- 示例:
- worker_processes 4;
- worker_processes auto;
- worker_cpu_affinity
- worker_cpu_affinity cpumask1 cpumask2…
- 解释:将每个worker子进程与我们的CPU物理核心绑定
- 示例:
- worker_cpu_affinity 0001 0010 0100 1000; # 4个物理核心,4个worker子进程
- worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 001000000100000010000000; # 8物理核心,8个worker子进程
- worker cpu affinity 01 10 01 10; # 2个物理核心,4个子进程
- 备注:将每个worker子进程与特定CPU物理核心绑定,优势在于:避免同一个worker子进程在不同的CPU核心上切换,缓存失效,降低性能;其并不能真正的避免进程切换
- worker_priority
- worker_priority number
- 解释:指定worker子进程的nice值,以调整运行nginx的优先级,通常设定为负值,以优先调用 nginx
- 示例:
- worker_priority -10;
- 备注:Linux默认进程的优先级值是120,值越小越优先;nice设定范围为-20到+19
- worker_shutdown_timeout time
- 解释:指定worker子进程优雅退出时的超时时间
- 示例:worker_shutdown_timeout 5s;
- timer_resolution interval
- 解释:worker子进程内部使用的计时器精度,调整时间间隔越大,系统调用越少,有利于性
能提升;反之,系统调用越多,性能下降 - 示例:worker_resolution 100ms;
- 解释:worker子进程内部使用的计时器精度,调整时间间隔越大,系统调用越少,有利于性
- daemon on|off
- 解释:设定nginx的运行方式,前台还是后台,前台用户调试,后台用于生产
- 示例:daemon off;
2 ) 针对部分字段的详解
-
worker_rlimit_nofile
- 代表我们每一个 worker 子进程能够打开的最大文件句柄数
- linux上可以通过
ulimit
命令来看当前用户可以打开的一个文件句柄数 - linux的一个哲学就是一切皆文件
- 对于每一个进程来说,它能够打开的文件句柄数也是有限制的
- 这是linux自身所固有的一个特性,对于 Nginx 进程来说,尤其是高并发的系统的而言
- 我们在这儿必须要对这样一个参数进行一个设置,默认配置下,它跟我们系统的参数应该是一致的
- 比如说
worker_rlimit_nofile 20480
对于每一个用户,他所打开的文件具柄数也是不一样的 - 对于work子进程来说,在上面,比如说我们指定
user nginx nginx
- 它就使用 nginx 用户和组 默认打开的文件句柄数
- 上面的设置,意味着每一个worker子进程能够打开的最大文件句柄数就是 20480
- 其实对于任何一个用户请求发送过来以后,真正处理这些用户请求的都是我们的这个worker子进程
- 对于worker 子进程来说,每一个连接都会发起一个TCP连接
- TCP连接之后,它会发送很多这样一些数据包文。
- 对于每一个连接来说,至少都需要一个套接字来维持这样一个连接
- 其实一个套接字可以理解成它就是一个文件句柄。
- 所以说,我们的Nginx想要承载更大的并发量的话,肯定把这个参数调高
- 如果 worker_processes 设置成 4,每个 woker 子进程设置为 20480
- 那整个 Nginx 服务器所能够承载的最大并发数应该是 20480 * 4
- 大概就是十一万多的并发量,但是这个只是一个理论上的数值
- 对 nginx 自身来说的话,它并不能够承受这样的并发量
- 我们通常单台服务器来说,对于 nginx 这样一个用户来说
- 它最大能够打开的文件句柄数都是有限制的,最多是 65535
- 这是我们linux的自身的一个最大限制,也就是说我们整个操作系统来说
- 最大可以打开的文件句柄数,也就是 65535
- 也就是说,这样一个 woker 子进程所能够打开的文件句柄数,即使设置了4个,最多也就是 65535
- 所以,这里写 20480 其实是不可能真正达到 20480 * 4 这么多并环连接的
- 但是在很多的一些配置文件中,这可能不止写一个 20480,可能写五万甚至十万的都有
- 这意味着超额设置最起码来说,对于高并发系统,不会达到我们的瓶颈
- 虽然我们达不到,但是,它会充分使用 linux 自身的一个特性去达到一个最顶峰的一个性能
-
worker_rlimit_core
- 用来记录我们的woker子进程因为某一些原因异常终止后或重启的日志
- 这就是说我们如果设定了这样一个参数之后,我们的worker 子进程在异常终止之后
- 会自动去记录这样一个 core 文件,我会把一些异常信息给记录到这样一个 core 文件中
- 便于开发人员或者是运维人员在发现这样一个情况之后可以查找具体的原因
- 查到是哪些原因,导致了我们的worker 子进程出现上述问题,便于进行改进
- 参数 size 表示这样一个文件的大小
- 默认情况下,因为我们不指定 worker_rlimit_core,它是不会记录这样一个 core 文件的
- 同时,还需要指定 core 文件的目录,也就是通过 worker_directory 指定的目录
- 这里有个问题,在我们的 worker 子进程中,比如上面指定了它启动的是 nginx 用户
- 对真正情况下来说,我们都知道我们的master进程是root用户运行的
- 但是真正处理用户请求的是 worker子进程 和 nginx用户
- 这又意味着你指定的目录, 对Nginx用户来说,必须是具有写权限的
- 保证我们的worker子进程,能够在异常终止之后,将正常的一个信息给写入到这样一个目录下
-
worker_processes
- 这个我们可以有两种用法
- 第一种可以直接指定个数,直接只写一个number,比如说,直接指定4
- 那第二种情况下,在Nginx的高版本中都有一个auto的属性
- 也就是, worker_processes 会自动启动,跟我们当前物理CPU核心的个数一致的一个子进程
- 把它设定auto之后,我们就不需要去管了
- 只有Nginx服务自动去探测当前服务器上CPU的物理核心个数
- 从而它将它设定为和物理核心个数一致的子进程
-
worker_cpu_affinity
- 也就是将我们的每一个worker子进程程与我们的CPU物理核心进行一个绑定
- 来看下图,对于我们当前的nginx进程来说呢,它会启动一个 master process 管理进程
- 这个管理进程只是用来维护我们的work子进程,更新配置文件
- 并将新的配置文件信息传递给我们的work子进程,但是它并不会真正的处理用户请求
- 真正处理用户请求的是下面主进程复制出来的几个work子进程
- 那假如说现在当前有4个work子进程,不管是什么进程,在CPU上都不可能是永远运行的
- 也就是说这个CPU不可能永远归你一个人, 每一个进程需要去CPU上申请CPU资源运行的时候
- 它通常会根据自己当前的一个优先级,在CPU上申请一个时间片
- 时间线就是一个时间, 申请了这样一个时间片之后,它就开始等待CPU的调度
- 在某一时刻CPU可能说OK你现在可以运行了,这个时候我们的worker子进程就会被CPU所调度上去
- 从而使用当前CPU的资源进行一个计算,到了一定的时间片完了之后
- 我们当前这个进程必须从当前的CPU上给退下来,或者是进入睡眠状态
- 或者是去处理一些等待一些其他的事情,总之,这个CPU他是不能再用了
- 因为这个CPU可能需要被其他的进程进行调度使用
- 现在就有一个问题,比如说我们的 worker 子进程对于多核心的服务器架构来说
- 比如说我们现在有4个物理核心和我们的worker子进程数量一样的物理核心
- 比如说当前某一个时刻,我们这个worker 子进程,编号为 A, 其他的为 B,C,D
- 比如说我们的 1号CPU 被A所调度了,A在1号CPU上运行了一段时间之后
- 由于时间片到了,它必须要从1号CPU上切换下来,一号CPU就被其他的一些进程调度了
- 可能不是我们的 A 号worker子进程,被其他linux的进程抢走去运行其他资源
- 一段时间之后,我们的worker子进程了,又申请到时间片了,又要去在CPU上运行了
- 这个时候,可能不是1号CPU去调度A这个worker子进程了
- 可能这个时候A申请到的是第三颗CPU,这个时候,它再去第三个CPU上进行运行
- 运行一段时间之后,它再下来,这样来回的在每一个CPU之间切换
- 这样有一个最大的问题,频繁的这些从CPU上运行又下来调度给其他进程这个进程切换的过程中
- 注意,进程切换在很多地方也称为上下文切换,对于这样一些进程切换来说都是有性能开销的
- 我们应该在一种理想的情景下尽可能的减少这些进程切换,虽然进程切换不可避免
- 但是我们如果能够尽可能的降低这些技能切换并充分利用单颗CPU的缓存,就可以大幅提高性能
- 现在,我们需要将1号CPU和 A这个worker子进程绑定在一块
- 同样,2号CPU和B, 3号和C,4号和D绑定在一块
- 绑定完之后,比如说A这个worker子进程就永远只能被一号CPU所调度
- 这样的好处是,A不再会与2,3,4号CPU绑定,只会等待1号的调度
- 我们每一个worker子进程所使用的CPU缓存来说,它永远是有效的
- 假如我们不把worker子进程和CPU核心进行绑定的话,不同CPU的调度缓存会失效
- 所以,把 worker 子进程和CPU绑定一个最大的好处就是尽可能的是减少CPU缓存的失效,性能降低
- 但是它并不能避免进程切换,因为进程切换是CPU的一个特性
- 对于每一个进程来说都不可能完全的去拥有CPU
- 尤其是对现在的一个linux操作系统来说,它是一个多任务的操作系统
- 这样的绑定,也是充分利用多核心的一个特性
- 我们看它的一个绑定指令的一个写法,上面示例中的 cpumask 是用掩码形式来标记cpu核心
worker_cpu_affinity 0001 0010 0100 1000;
4个物理核心和4个worker子进程,每个物理核心与一个子进程绑定- 0001 表示第一颗,0010 表示第二颗,0100 表示第三颗,1000 表示第四颗
worker cpu affinity 01 10 01 10;
这里表示,2个物理核心,4个子进程- 这时要: 2个子进程共享1个物理核心
- 上述表示:A 和 C 共享1号,B和D 共享2号核心
-
worker_priority
- 这个参数主要是用来指定我们worker子进程中的一个nice值,以调整运行Nginx优先级
- 通常设定为负值,以优先调用Nginx, 在 linux上可以通过调整nice值
- 从而去改变一个进程运行的优先级,从而尽可能的被调度
- 进程优先级越高,它就尽可能的被CPU所调度
- linux本身默认的进程优先级是 120,值越小,越优先
- 而 nice 值设定的范围是 -20 ~ +19
- 这样,值为 100 是最优先的,值为 139 是排到最后的
- 我们设置 worker_priority -10, 这样就保证我们的每一个worker子进程的优先级都是比较高的
- 从而尽可能的被CPU所调度使用,从而保证我们的Nginx处理的高性能
- 这个在实际的生产环境中,通常把这个设定的一个负值就行了
- 这个需要根据你自身这个操作系统上很多的应用程序来判断,当然,Nginx服务器一般只跑Nginx
- 这样能够保证Nginx承载很高并发量,我们不会选择在Nginx服务器上跑其他的应用程序
-
worker_shutdown_timeout
- 用来指定我们的 worker 子进程优雅的退出的超时时间
- 比如在 Nginx 的热部署中,在更新了配置文件,包括我们的程序文件信息之后
- 我们需要给worker子进程发送一个什么指令, 我们会给他发送一个优雅退出的指令
- 优雅退出表示,我们的 worker 指令不会主动的去关闭客户端所发送过来的连接
- 它会优先将客户端的连接给请求处理完之后去等待客户端关闭连接
- 那这样就存在一种情形,比如说我们的客户端,因为一些情况的异常了
- 他这个客户端发起TCP请求之后,它与我们Nginx服务器建立了一个TCP连接
- 建立完TCP连接之后,需要发送对应的HTTP请求
- 需要将HTTP的header信息发送到服务器,服务器收到之后再响应
- 某些情况下,它建立一个连接之后,就是不发送请求
- 或者是发送了一个请求之后,得到我们服务器的回应之后,它就是不返回确认的数据包
- 为了应对这样一种情形,防止Nginx服务器被很多恶意程序所攻击
- 比如说很多恶意程序的故意发送这样一个数据报文,它就是不跟你确认,不让你关闭
- 如果永远不关闭的话,这个连接就相当于是永远不会释放了,这会严重的影响我们的Nginx性能
- 这个时候, 就需要去设定一个 worker_down_timeout
- 也就是说, 我在给你发送完正常的回应之后,我会等待这个强制性的时间,比如说五秒内
- 如果五秒之后,不管你有没有回应,或主动断开,我会我强制你断开,用以防止攻击
-
timer_resolution
- worker 子进程内部使用的计算器精度,比如设成5毫秒
- 比如说,我们的Nginx自身运行的过程中,它会向我们的内核态发起很多系统调用
- 比如说,它要去获取时间计时器,上面这个参数,既然定义了5秒超时时间
- 肯定要有一个计时器,这个计时器通过获取内核态的一个系统时间
- 对所有的用户进程需要向内核态发起系统调用的时候,这个对性能来说是有很大的影响的
- 每一次用户态向内核态的切换都会降低性能,所以,需要把这个时间间隔尽可能设置的小一点
- 当然如果说你这个应用本身对实间精度要求特别高的话,需要去把这个值设的尽可能大
- 一般在情况下会把这个值设置小一点,参考如下 客户端请求处理流程
- 假设右边这张图就是我们的Nginx服务器,它启动了4个worker子进程
- 用来响应客户端发送过来的用户请求,Nginx 自身是运行的用户态的
- 我们的应用程序是七层的,它肯定是运行在我们的用户态的
- 我们都知道操作系统是有内核态的,内核态处理什么?
- 比如说,TCP/IP协议中的数据包都是由内核态来做处理的
- 再比如说,我们需要磁盘IO了,在操作系统获取系统时间等等这样一些系统调用
- 包括我们去获取磁盘上的文件信息,我们打开某一个文件,它的一个应用程序
- 需要向我们内核态的一个read函数发起一个系统调用
- 从而由我们的内核态去和我们磁盘上的文件打交道,把内容再返回给我们用户态进程
- 所以说,对于这样一个用户请求,比如说我们现在有一个用户
- 他发送一个TCP连接打到我们的网卡上,网卡收到之后,把这个TCP连接拆包给TCP/IP协议
- TCIP协议能够解开这样一个数据包,如果网卡把二层的数据包给拆掉之后
- 它发现一些三层的信息,比如说是否是当前的IP,如果是当前的IP再继续处理
- 如果不是当前的IP,TCP/IP协议栈会直接将这个数据包给丢掉
- 如果发现这个三层的这个IP数据包是自己的IP的话,它会继续拆包到四层
- 再看这个端口是谁,发现是向80端口发起的服务请求。
- 这个时候,我们的内核态会知道 80端口就是监听在用户态的某一个应用程序所提供的服务
- 比如说,我们的Nginx服务, 这时候TCP/IP协议栈会将这样一个用户请求的这个数据包
- 继续扔给我们这个用户态空间的worker子进程,由worker子进程具体去处理
- 假如说我们需要频繁的去获取系统的时间或者是跟磁盘打交道等等
- 当一些用户态应用程序做不了一些事情的时候,比如计时器获取系统时间,就要发起系统调用
- 这时候就会和内核态来打交道,这种系统调用很耗费性能,如果说你的时间精度要求高的话
- 比如说,间隔可能是十毫秒,也就是十毫秒不停的需要有用户态切换到内核态
- 并且从内核态的切换到用户态,这样会极大影响性能
- 所以 timer_resolution 这个字段为了尽可能减少应用程序和内核态之间的频繁切换来节约性能
-
daemon
- 是一个守护进程的参数, 如果值是 on 就代表Nginx运行方式是在后台,也就是默认的方式
- 如果说在一些调试情况下,在开发的过程中,可能会遇到很多很多的问题。
- 需要打开调试模式的时候,我们可以将这个这样一个参数设定为 off
- 这个时候它会运行在前台将很多的错误信息给你输出
- 当然这个参数需要配合其他很多的一些调试参数来使用啊
- 其实这个参数我们用的也很少,它能够供我们来调试debug使用
具体配置参考
1 ) nginx.conf
user nginx nginx;
worker_processes auto;
pid /opt/nginx/logs/nginx.pid;
worker_rlimit nofile 12500;
worker_rlimit core 50M;
working_directory /opt/nginx/tmp;
worker_cpu_affinity 0001 0010 0100 1000;
worker_priority -10;
worker_shutdown_timeout 5s;
timer_resolution 100ms;
daemon on;
2 ) 调试过程可能需要的一些辅助命令
- 创建子进程工作目录并设置权限
- $
mkdir /opt/nginx/tmp
- $
chown -R nginx.nginx /opt/nginx/tmp
- $
- 查看cpu的信息 $
lscpu
- 检查配置 $
/opt/nginx/sbin/nginx -t
- 加载配置 $
/opt/nginx/sbin/nginx -s reload
- 调试辅助
- $
ps -ef | grep nginx
- $
kill 7271
这里可能是 nginx root 的pid进程,可以先终止 - $
/opt/nginx/sbin/nginx
终止后,启动nginx - $
cat /opt/nginx/logs/nginx.pid
- $