1、Nginx 是什么?

Nginx 是⼀个HTTP服务器, 也是⼀个反向代理服务器
正向代理:是位于客户端和原始服务器之间的服务器,客户端向代理发送一个请求并指定原始服务器,然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。
zhengx.jpg
反向代理:在服务器端接收客户端的请求,然后把请求分发给具体的服务器进行处理,然后再将服务器的响应结果反馈给客户端。
fanx.jpg
正向代理客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的IP地址,还有代理程序的端口。
反向代理正好与正向代理相反,对于客户端而言代理服务器就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间中的内容发送普通请求,接着反向代理将判断向哪个原始服务器转交请求,并将获得的内容返回给客户端。

2、Nginx 特点

  • 高性能
  • 配置项丰富,可以对流量进⾏控制,分发,变更,记录等
  • ⽀持插件,⾼扩展性
  • C 语⾔实现,相对⽐较复杂
  • 跨平台:Nginx 可以在大多数 Unix like OS 编译运行,而且也有 Windows 的移植版本
  • 内置的健康检查功能:如果 Nginx Proxy 后端的某台 Web 服务器宕机了,不会影响前端访问

    2.1 Nginx 的内部模型及处理过程

    Nginx 的内部(进程)模型:
    jiagou.jpg

    2.1.1 nginx 启动后,会有一个 master 进程和多个 worker 进程

    master 进程主要用来管理 worker 进程,包括:

  • 接收来自外界的信号

  • 向各 worker 进程发送信号
  • 监控 worker 进程的运行状态
  • 当 worker 进程退出后(异常情况下),会自动重新启动新的 worker 进程

而 worker 进程主要处理基本的网络事件:

  • 多个 worker 进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的
  • 一个请求,只可能在一个 worker 进程中处理,一个 worker 进程,不可能处理其它进程的请求
  • worker 进程的个数是可以设置的,一般我们会设置与机器 cpu 核数一致

    2.1.2 Master 接收到信号以后怎样进行处理 (./nginx -s reload)?

    master 进程在接到信号后,会先重新加载配置文件,然后再启动新的进程,并向所有老的进程发送信号,告诉他们可以光荣退休了。新的进程在启动后,就开始接收新的请求,而老的进程在收到来自 master 的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出

    2.1.3 worker 进程又是如何处理请求的呢?

  1. 每个 worker 进程都是从 master 进程fork(分配)过来,在 master 进程里面,先建立好需要 listen 的 socket 之后,然后再 fork 出多个 worker 进程,这样每个 worker 进程都可以去 accept 这个 socket (当然不是同一个socket,只是每个进程的这个socket会监控在同一个ip地址与端口,这个在网络协议里面是允许的)
  2. 当一个连接进来后,所有在 accept 在这个 socket 上面的进程,都会收到通知,而只有一个进程可以 accept 这个连接,其它的则 accept 失败,这是所谓的惊群现象。nginx 提供了一个 accept_mutex 的共享锁,有了这把锁之后,同一时刻,就只会有一个进程在 accpet 连接,这样就不会有惊群问题了
  3. 当一个 worker 进程在 accept 1这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接。一个请求完全是由worker进程来处理,而且只在一个worker进程中处理

2.1.4 nginx 采用这种进程模型有什么好处呢?

采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master 进程则很快重新启动新的 worker 进程。当然,worker 进程的异常退出,肯定是程序有 bug 了,异常退出,会导致当前 worker 上的所有请求失败,不过不会影响到所有请求,所以降低了风险

2.2 Nginx 如何处理一个请求?

  1. 首先,nginx 在启动时,会解析配置文件,得到需要监听的端口与 ip 地址,然后在 nginx 的 master 进程里面,先初始化好这个监控的 socket
  2. 然后再 fork(一个现有进程可以调用fork函数创建一个新进程。由 fork 创建的新进程被称为子进程 )出多个子进程出来
  3. 客户端向 nginx 发起连接。当客户端与 nginx 进行三次握手,与 nginx 建立好一个连接后,此时,某一个子进程会 accept 成功,得到这个建立好的连接的 socket。然后创建 nginx 对连接的封装,即 ngx_connection_t结构体
  4. 设置读写事件处理函数并添加读写事件来与客户端进行数据的交换
  5. 最后,nginx 或客户端来主动关掉连接,到此,一个连接就寿终正寝了

nginx 在实现时,是通过一个连接池来管理的,每个 worker 进程都有一个独立的连接池,连接池的大小是 worker_connections。这里的连接池里面保存的其实不是真实的连接,它只是一个 worker_connections 大小的一个 ngx_connection_t 结构的数组。并且,nginx 会通过一个链表 free_connections 来保存所有的空。ngx_connection_t,每次获取一个连接时,就从空闲连接链表中获取一个,用完后,再放回空闲连接链表里面。
worker_connections 是表示每个 worker 进程所能建立连接的最大值,所以,一个 nginx 能建立的最大连接数,应该是 worker_connections worker_processes。如果是 HTTP 作为反向代理来说,最大并发数量应该是worker_connections worker_processes/2。因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,会占用两个连接

3、Nginx 基本命令

  • 启动nginx: nginx
  • 以特定目录下的配置文件启动:nginx -c /特定目录/nginx.conf
  • 重新加载配置:nginx -s reload 执行这个命令后,master进程会等待worker进程处理完当前请求,然后根据最新配置重新创建新的 worker 进程,完成 Nginx 配置的热更新。
  • 立即停止服务: nginx -s stop
  • 从容停止服务: nginx -s quit 执行该命令后,Nginx 在完成当前工作任务后再停止。
  • 检查配置文件是否正确 : nginx -t
  • 检查特定目录的配置文件是否正确: nginx -t -c /特定目录/nginx.conf
  • 查看版本信息: nginx -v

    4、Nginx 默认的配置文件

    主要有 main、events、http、server、location 五个块组成。其中 http 、server、location 属于嵌套关系

  • main:主要控制 Nginx 子进程所属的用户和用户组、派生子进程数、错误日志位置与级别、pid 位置、子进程优先级、进程对应 cpu、进程能够打开的文件描述符数目等

  • events:控制 Nginx 处理连接的方式
  • http:Nginx 处理 http 请求的主要配置块
  • server:Nginx 中主机配置块,可用于配置多个虚拟主机
  • location:server 中对应目录级别的控制块,可以有多个

    5、Nginx 在线上流量的位置

    weizhi.jpg

    6、Nginx 重要参数

    6.1 worker_processes

    工作进程数,默认是1。作者原话:

  • 一般一个进程足够了,你可以把连接数设得很大。

  • 如果有 SSL、gzip 这些比较消耗 CPU 的工作,而且是多核 CPU 的话,可以设为和 CPU 的数量一样
  • 或者要处理很多很多的小文件,而且文件总大小比内存大很多的时候,也可以把进程数增加,以充分利用IO带宽(主要似乎是 IO 操作有 block )

    6.2 worker_rlimit_nofile

    worker 进程最大打开文件数

    6.3 worker_connections

    单个工作进程可以允许同时建立外部连接的数量,默认是1024

  • 这个值不是随便设置的,与内存和操作系统级别的“进程最大可打开文件数”有关。

  • 内存:每个连接数分别对应一个 read_event、一个 write_event 事件,一个连接数占用232字节,2个事件总占用96字节,那么一个连接总共占用328字节,通过数学公式可以算出 100000 个连接数大概会占用 100000 * 328 / 1024 / 1024 = 31M,当然这只是 nginx 启动时,connections 连接数所占用的 nginx

    6.4 log_format

    用来定义记录日志的格式。log_format 格式变量:

  • $remote_addr #记录访问网站的客户端地址

  • $remote_user #远程客户端用户名
  • $time_local #记录访问时间与时区
  • $request #用户的http请求起始行信息
  • $status #http状态码,记录请求返回的状态码,例如:200、301、404等
  • $body_bytes_sent #服务器发送给客户端的响应 body 字节数
  • $http_referer #记录此次请求是从哪个连接访问过来的,可以根据该参数进行防盗链设置。
  • $http_user_agent #记录客户端访问信息,例如:浏览器、手机客户端等

一般来说:nginx 的 log_format 有很多可选的参数用于指示服务器的活动状态,默认的是:

  1. log_format main '$remote_addr - $remote_user [$time_local] "$request" '
  2. '$status $body_bytes_sent "$http_referer" '
  3. '"$http_user_agent" "$http_x_forwarded_for"';

附 Nginx 中文文档:https://www.nginx.cn/doc/index.html