7. Nginx 工作机制&参数设置(详细讲解说明)

7. Nginx 工作机制&参数设置(详细讲解说明)

@[toc]


1. Nginx 当中的 master-worker 机制原理

master-worker 工作原理图:

一个 master 管理多个 worker

sh 复制代码
[root@localhost ~]# cd /usr/local/nginx/
[root@localhost nginx]# ls
auto     CHANGES.ru        conf       contrib       html     logs      man   proxy_temp  sbin       src
CHANGES  client_body_temp  configure  fastcgi_temp  LICENSE  Makefile  objs  README      scgi_temp  uwsgi_temp
[root@localhost nginx]# ./sbin/nginx 
[root@localhost nginx]# ps -aux | grep nginx
root       2610  0.0  0.0  20572   664 ?        Ss   16:55   0:00 nginx: master process ./sbin/nginx
nobody     2611  0.0  0.0  23040  1416 ?        S    16:55   0:00 nginx: worker process
root       2613  0.0  0.0 112812   976 pts/0    S+   16:55   0:00 grep --color=auto nginx
[root@localhost nginx]# 

2. master-worker 机制

争抢机制示意图:

上述示意图图解:

  1. 一个 master Process 管理多个 worker process ,也就是说 Nginx 采用的是多进程结构 ,而不是多线程结构。
  2. 当 Client 客户端发出请求(任务)时,master Process 会通知管理的 worker Process
  3. worker Process 开始争抢任务,争抢到的 worker Process 会开启连接,完成任务。
  4. 每个 worker 都是一个独立的进程,每个进程里只有一个主线程
  5. Nginx 采用了 IO 多路复用机制(需要在 Linux 环境),使用 IO 多路复用机制,是 Nginx 在使用为数不多的 worker Process 就可以实现高并发的关键。
  1. Client 客户端发送请求给了 Nginx 的 Master

  2. Master 将请求信息,发送给所有的 worker,告知这些 worker ,有请求信息来了,大家快争抢,及时反馈

  3. Master 下发请求了,所有的 worker 开始争抢,虽然所有的 worker 都会争抢但是,只有一个 worker 才可以争抢成功。

  4. 当其中一个 worker 争抢成功了,则就会通过 Nginx 反向代理,将请求转发给上游的Tomcat 服务器/其他微服务处理该请求。

Master-Worker 模式:

上述图解:Master-Worker 模式讲解说明:

  1. Nginx 在启动后,会有一个 master 进程和多个相互独立的 worker 进程。
  2. Master 进程接收到来自外界的信号,向各个 worker 进程发送信号,每个进程都有可能来处理这个连接。
  3. Master 进程能监控 Worker 进程的运行状态,当 worker 进程退出后(异常情况下),会自动启动新的 worker 进程。以此来保证服务系统的稳定性。

2.1 accpet_mutex 解决"惊群现象"

  1. 当所有子进程都继承了父进程的 sockfd ,当连接进来时,所有子进程都将收到通知并**"争抢"** 与 它建立连接,这就叫 "惊群现象"
  2. 这里的"争抢",虽然大量的进程被激活来进行干活,争抢处理请求,但是最后最后,只会有一个进程争抢成功(只有一个进程可以 accept() 到这个连接),从而处理该请求,而其他的进程被激活了,发现没有争抢到,就又挂起了。这样导致大量的进程被激活又挂起,会消耗系统资源。
  3. Nginx 提供了一个 accept_mutex ,这是一个加在 accept 上的一把共享锁。即每一个 worker 进程在执行 accept 之前都需要先获取锁,获取不到就放弃执行 accept()。有了这把锁之后,同一时刻,就只会有一个进程去 accept() ,就不会有惊群问题了。简单的说就是:加锁,当获取到锁的进程才会被激活启用,其他没获取到锁的进程则不会来争抢资源了。
  4. 当一个 worker 进程在 accept() 这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,完成一个完整的请求。
  5. 一个请求,完全由 worker 进程来处理,而且只能在一个 worker 进程中处理。

用多进程结构而不用多线程结构的好处/理论:

  1. 节省锁带来的新的开销问题,每个 worker 进程都是独立的进程,不共享资源,不需要加锁。在 编程以及问题查上时,也会方便很多。

  2. 独立进程,减少风险。采用独立的进程,可以让互相之间不会影响,一个进程退出后, 其它进程还在工作,服务不会中断,master 进程则很快重新启动新的 worker 进程

Nginx 实现高并发的秘密-IO 多路复用

  1. 对于 Nginx 来讲,一个进程只有一个主线程,那么它是怎么实现高并发的呢?
  2. 采用了 IO 多路复用的原理,通过异步非阻塞的事件处理机制,epoll 模型,实现了轻量 级和高并发。
  3. Nginx 是如何具体实现的呢,举例来说:每进来一个 request,会有一个 worker 进程去 处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后 端)服务器转发 request,并等待请求返回。那么,这个处理的 worker 不会这么傻等着,他 会在发送完请求后,注册一个事件:"如果 upstream 返回了,告诉我一声,我再接着干"。 于是他就休息去了。此时,如果再有 request 进来,他就可以很快再按这种方式处理。而 一旦上游服务器返回了,就会触发这个事件,worker 才会来接手,这个 request 才会接着 往下走。由于 web server 的工作性质决定了每个 request 的大部份生命都是在网络传输中, 实际上花费在 server 机器上的时间片不多,这就是几个进程就能解决高并发的秘密所在。换句话生活中的例子是:和我们生活中去餐厅点菜类似,当你进入到一个餐厅,就会出现一个服务员(这个服务员就是我们 worker 进程),询问你需要点什么菜。当你点完菜之后,服务员(我们 worker 进程)就离开了,去服务其他的客户了。但是当你的菜都被后厨做好之后(也就是这里我们的请求被后端的服务器处理好后),这时候服务员又来服务器你了(worker 就回来,将你处理好的请求返回给你)。将菜品都端上来给你。

2.2 Nginx 的 master-worker 工作机制的优势

  1. 支持 Nginx-s reload 热部署,这个特征在我们的 Spring Cloud 当中学习过。
  2. 对于每个 worker 进程来说,独立的进程,不需要加锁,所以省掉了所带来的开销,同时在编程以及问题的查找时,也会方便很多。
  3. 每个 worker 都是一个独立的进程,但每个进程里只有一个主线程,通过异步非阻塞的方式IO多路复用 来处理请求,即使是高并发请求也能应对。
  4. 采用独立的进程,互相之间不会影响,一个 worker 进程退出后,其它 worker 进程还在工作,服务不会中断,master 进程则很快启动新的 worker 进程。
  5. 一个 worker 分配一个 CPU,那么 worker 的线程可以把一个 CPU 的性能发挥到极致。

3. Nginx 参数设置

说明: 关于 Nginx 参数配置信息是在 nginx.conf 文件当中进行配置的。而 nginx.conf 文件存在两个目录位置。

  • 第一个:默认在 /usr/local/nginx/conf 路径下存在该 nginx.conf 文件。
  • 第二个:有时也在 usr/local/nginx 路径下也有一个 nginx.conf 文件,如果该路径下存在该文件,Nginx 优先读取的是该路径下的nginx.conf 文件,优先级比第一个目录下的文件更高。

3.1 worker_processes

需要设置多少个 worker 的设置

  1. 每个 worker 的线程可以把一个 CPU 的性能发挥到极致。所以 worker 数和服务器的 CPU数相等是最为适宜的 。如果我们设置少了则会浪费 CPU,设多了会造成 CPU 频繁切换上下文带来的损耗。
  2. 设置 worker 的数量,Nginx 默认没有开启利用多核 CPU ,可以通过 worker_cpu_affinity 配置参数充分利用多核 CPU 的性能。
properties 复制代码
#2 核 cpu,开启 2 个进程
worker_processes     2;
worker_cpu_affinity 01 10;



#2 核 cpu,开启 4 个进程,
worker_processes         4;
worker_cpu_affinity 01 10 01 10;



#4 核 cpu,开启 2 个进程,0101 表示开启第一个和第三个内核,1010 表示开启第二个和 第四个内核;
worker_processes         2;
worker_cpu_affinity 0101 1010;



#4 个 cpu,开启 4 个进程
worker_processes         4;
worker_cpu_affinity 0001 0010 0100 1000;


#8 核 cpu,开启 8 个进程
worker_processes         8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

补充: worker_cpu_affinity 理解

配置实例:

首先我们需要找到 Nginx 的配置文件 nginx.conf 文件。

这里我们的路径是在 /usr/local/nginx/conf 路径下:

properties 复制代码
  2 #user  nobody;
  3 worker_processes  2;
  4 worker_cpu_affinity 01 10;
  5 

配置好之后,我们需要执行 如下命令,检查校验我们的配置编写的在 nginx.conf 文件当中的内容是否存在错误。

sh 复制代码
[root@localhost conf]# ../sbin/nginx -t

重新加载 nginx ,读取我们配置的信息。

sh 复制代码
[root@localhost conf]# ../sbin/nginx

如果我们的 Nginx 已经启动了,可以执行如下命令,进行一个热部署读取我们的配置文件信息。

sh 复制代码
[root@localhost conf]# ../sbin/nginx -s reload
sh 复制代码
[root@localhost conf]# ps -aux | grep nginx # 查看我们的 Nginx 是否启动成功

3.2 worker_connection

  1. worker_connection 表示每个 worker 进程所能建立连接的最大值,所以,一个 Nginx 能 建立的最大连接数,应该是 worker_connections * worker_processes

  2. Nginx 默认是 : worker_connections: 1024

  3. 调大:worker_connections: 60000,(调大到 6 万连接)

  4. 同时要根据系统的最大打开文件数来调整。

properties 复制代码
系统的最大打开文件数>= worker_connections*worker_process
根据系统的最大打开文件数来调整,worker_connections 进程连接数量要小 于等于系统的最大打开文件数,worker_connections 进程连接数量真实数量= worker_connections * worker_process


查看系统的最大打开文件数 
ulimit -a|grep "open files"
open files (-n) 65535
sh 复制代码
[root@localhost conf]# ulimit -a|grep "open files"
  1. 根据最大连接数计算最大并发数: 如果是支持 http1.1 的浏览器每次访问要占两个连接, 所以普通的静态访问最大并发数是: worker_connections * worker_processes /2,而如果 是 HTTP 作 为 反 向 代 理 来 说 , 最 大 并 发 数 量 应 该 是 worker_connections * worker_processes/4。因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端 服务的连接,会占用两个连接, 看一个示意图。

3.3 配置 Linux 最大打开文件数

  1. 使用 ulimit -a 可以查看当前系统的所有限制值,使用 ulimit -n 可以查看当前的最大打 开文件数。
sh 复制代码
[root@localhost conf]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 14956
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 14956
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
  1. 使用 ulimit -n 65535 可即时修改,但重启后就无效 了。(注 ulimit -SHn 65535 等效 ulimit -n 65535-Ssoft-Hhard)
sh 复制代码
[root@localhost conf]# ulimit -n 65535
  1. 特别的,这里有其他的三种永久的修改方式:如下:

  2. /etc/rc.local 中增加一行 ulimit -SHn 65535

  3. /etc/profile 中增加一行 ulimit -SHn 65535

  4. /etc/security/limits.conf 最后增加如下两行记录

properties 复制代码
* soft nofile 65535
* hard nofile 65535

在 CentOS 中使用第 1 种方式无效果,使用第 3 种方式有效果,而在 Debian 中使用第 2 种 有效果

特别说明:

如果我们采用第三种方式,报了一个,重启报错时,我们可以进行如下操作进行解决:

/etc/pam.d/login配置文件,在最后添加以下一条内容:

properties 复制代码
session    required pam_limits.so


可以看到软限制和硬限制的值都修改成功了。

sh 复制代码
vim /etc/security/limits.conf
properties 复制代码
 59 #@student        -       maxlogins       4
 60 
 61 * soft nofile 65535
 62 * hard nofile 65535
 63 
 64 # End of file

重启 Linux 虚拟机,测试验证:

sh 复制代码
[root@localhost ~]# ulimit -a # 查看系统文件支持的最大连接数

4. 最后:

"在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。"

相关推荐
Lojarro3 分钟前
JavaEE基础之-session&cookie
java·java-ee
A boy CDEF girl5 分钟前
【JavaEE】线程安全
java·安全·java-ee
何政@10 分钟前
Mysql面试篇笔记:
java·数据库·spring boot·笔记·mysql·面试
工一木子19 分钟前
【Java基础】Java中new一个对象时,JVM到底做了什么?
java
馨谙32 分钟前
Exception in thread “main“ java.lang.ExceptionInInitializerError
java·开发语言
小杨40436 分钟前
springboot框架启动流程二(源码分析)
spring boot·后端·架构
caihuayuan441 分钟前
Golang的数据库分库分表
java·大数据·sql·spring·课程设计
柃歌43 分钟前
【UCB CS 61B SP24】Lecture 19 & 20: Hashing & Hashing II 学习笔记
java·数据结构·笔记·学习·算法
星尘库44 分钟前
基于SpringBoot的失物招领平台的设计与实现
vue.js·spring boot·后端·小程序
试着奔跑的菜鸟1 小时前
经验分享:用一张表解决并发冲突!数据库事务锁的核心实现逻辑
java·经验分享·高并发·分布式锁