Nginx (engine x) 是一个高性能的 Web 服务器和反向代理服务器,也是一个 IMAP/POP3/SMTP 服务器:
特点:

  • 反向代理,负载均衡器
  • 高可靠性、单master多worker模式
  • 高可扩展性、高度模块化
  • 非阻塞
  • 事件驱动
  • 低内存消耗
  • 热部署

    Nginx框架模型介绍

    进程组件角色

  • Master 进程:监视工作进程的状态;当工作进程死掉后重启一个新的;处理信号和通知工作进程。

  • Worker 进程:处理客户端请求,从主进程处获得信号做相应的事情。
  • Cache Loader 进程:加载缓存索引文件信息,然后退出。
  • Cache Manager进程:管理磁盘的缓存大小,超过预定值大小后最少使用数据将被删除。

image.png

Nginx 服务器启动后,产生一个 Master 进程(Master Process),Master 进程执行一系列工作后产生一个或者多个 Worker 进程(Worker Processes)。其中,Master 进程用于接收来自外界的信号,并向各 Worker 进程发送信号,同时监控 Worker 进程的工作状态。当 Worker 进程退出后(异常情况下),Master 进程也会自动重新启动新的 Worker 进程。Worker 进程则是外部请求真正的处理者。
多个 Worker 进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个 Worker 进程中处理,一个 Worker 进程不可能处理其它进程的请求。Worker 进程的个数是可以设置的,一般我们会设置与机器 CPU 核数一致。同时,Nginx为了更好的利用多核特性,具有 CPU 绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效(CPU affinity)。所有的进程的都是单线程(即只有一个主线程)的,进程之间通信主要是通过共享内存机制实现的。

Nginx处理请求

Worker进程之间是平等的,每个进程处理请求的机会也是一样的。当我们提供80端口的 HTTP 服务时,一个连接请求过来,每个进程都有可能处理这个连接
image.png
每个 Worker 进程都是从 Master 进程fork过来,在 Master 进程里面,先建立好需要 listen 的 socketlistenfd)之后,然后再 fork 出多个 Worker 进程。所有 Worker 进程的 listenfd 会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有 Worker 进程在注册 listenfd 读事件前抢互斥锁accept_mutex,抢到互斥锁的那个进程注册 listenfd 读事件,在读事件里调用 accept 接受该连接。当一个 Worker 进程在 accept 这个连接之后,就开始读取、解析、处理请求,在产生数据后再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求完全由 Worker 进程来处理,而且只在一个 Worker 进程中处理。

  • master建立socket及文件描述符
  • master fork出多个worker进程
  • 事件到来,worker争抢互斥锁accept_mutex
  • 抢到互斥锁的进程注册文件描述符,读事件
  • 在读事件中调用accept接受连接

    优势

  1. 对于每个 Worker 进程来说,独立的进程不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。
  2. 采用独立的进程可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断, Master 进程则很快启动新的 Worker 进程。
  3. Worker 进程异常退出,肯定是程序出现了bug,异常退出会导致当前 Worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。

    cache loader和cache manager

    缓存加载器进程(Cache Loader ) 进程是在Nginx服务启动一段时间后由主进程生成,在缓存元数据重建完成后就自动退出。缓存管理器进程(Cache Manager)进程一般存在于主进程的整个生命周期,负责对缓存索引进行管理。通过缓存机制,可以提高对请求的响应效率,进一步降低网络压力。
    image.png

    热升级

    热升级是指在不停止服务的情况下更换 Nginx 的binary文件。热升级会经历以下几个步骤:

  4. 把旧的 Nginx binary 文件替换为新的

  5. 现有老的 Master (Old) 进程发生 USR2 信号,之后 Master (Old) 进程会将修改 pid 文件名,添加 后缀 .oldbin。
  6. 使用新的 binary 文件启动新的 Master (New) 进程,Master (New) 进程会自动启动新的 Worker 进程。(Master (New) 进程其实是老的 Master(Old) 进程的子进程,不过这个子进程是使用了新的 binary 文件带入来启动的。)
  7. 向 Master(Old) 进程发送 QUIT 信号。

整个过程中,Master(Old) 进程是一直存活的,这是为了方便我们进行回滚,也就是发现新的 Nginx 程序有问题了,这个时候因为 Master(Old) 进程还在,可以向 Master(Old) 进程发送 HUP 信号,相当于执行了一次 reload,会启动新的 Worker 进程,然后再向 Master (New) 进程发送 QUIT 信号,也就是要求新的 Worker 进程优雅退出,就实现了回滚。
在一个父进程退出,而它的一个或多个子进程还在运行时,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并由 init 进程对它们完成状态收集工作。所以老 Master(Old) 进程退出后,新的 Master(Old) 进程并不会退出。

Nginx的应用场景

正向代理

正向代理其实就是说客户端无法主动或者不打算主动去向某服务器发起请求,而是委托了 Nginx 代理服务器去向服务器发起请求,并且获得处理结果,返回给客户端。
image.png

  1. resolver 114.114.114.114 8.8.8.8;
  2. server {
  3. resolver_timeout 5s;
  4. listen 81;
  5. location / {
  6. proxy_pass http://$host$request_uri;
  7. }
  8. }

resolver是配置正向代理的DNS服务器,listen 是正向代理的端口,配置好了就可以在浏览器上面或者其他代理插件上面使用服务器ip+端口号进行代理了。

反向代理

反向代理( Reverse Proxy )方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。正向代理在客户端侧,反向代理在服务端侧。
image.png
简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已。下面贴上一段简单的实现反向代理的配置:

  1. server {
  2. listen 80;
  3. server_name localhost;
  4. location / {
  5. proxy_pass http://www.google.com;
  6. proxy_set_header Host $http_host;
  7. proxy_set_header X-Real-IP $remote_addr; #获取客户端真实IP
  8. }
  9. }

保存配置文件后启动 Nginx,这样当我们访问 http://localhost 的时候,就相当于访问 http://www.google.com 了。

负载均衡

负载均衡其意思就是分摊到多个操作单元上进行执行,例如:Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。简单而言就是当有2台或以上服务器时,根据规则将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡
而Nginx目前支持自带3种负载均衡策略,还有2种常用的第三方策略:

  1. 轮询(RR):默认的策略。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器宕掉,能自动剔除。
  2. 权重(weight):可以给不同的后端服务器设置一个权重值(weight),用于调整不同的服务器上请求的分配率。权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器硬件配置进行调整的。
  3. ip_hash:每个请求按照发起客户端的 ip 的 hash 结果进行匹配,这样的算法下一个固定 ip 地址的客户端总会访问到同一个后端服务器,这也在一定程度上解决了集群部署环境下 Session 共享的问题。
  4. fair:智能调整调度算法,动态的根据后端服务器的请求处理到响应的时间进行均衡分配。响应时间短处理效率高的服务器分配到请求的概率高,响应时间长处理效率低的服务器分配到的请求少。Nginx 默认不支持 fair 算法,如果要使用这种调度算法,请安装 upstream_fair 模块。
  5. url_hash:按照访问的 URL 的 hash 结果分配请求,每个请求的 URL 会指向后端固定的某个服务器,可以在 Nginx 作为静态服务器的情况下提高缓存效率,示例如下。同样要注意 Nginx 默认不支持这种调度算法,要使用的话需要安装 Nginx 的 hash 软件包。

    1. upstream #自定义组名 {
    2. server x1.google.com; #可以是域名
    3. server x2.google.com;
    4. #server x3.google.com
    5. #down 不参与负载均衡
    6. #weight=5; 权重,越高分配越多
    7. #backup; 预留的备份服务器
    8. #max_fails 允许失败的次数
    9. #fail_timeout 超过失败次数后,服务暂停时间
    10. #max_coons 限制最大的接受的连接数
    11. #根据服务器性能不同,配置适合的参数
    12. #server 106.xx.xx.xxx; 可以是ip
    13. #server 106.xx.xx.xxx:8080; 可以带端口号
    14. #server unix:/tmp/xxx; 支出socket方式
    15. }

    http服务器

    Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,首先看看Nginx做静态资源服务器。

    1. server {
    2. listen 80;
    3. server_name localhost;
    4. location / {
    5. root /root/website/;
    6. index index.html;
    7. }
    8. }

    动静分离

    动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。

    1. upstream dynamic_server{
    2. server 192.168.0.2:8080;
    3. server 192.168.0.3:8081;
    4. }
    5. server {
    6. listen 80;
    7. server_name localhost;
    8. location / {
    9. root /root/website/;
    10. index index.html;
    11. }
    12. # 所有静态请求都由nginx处理,存放目录为html
    13. location ~ \.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
    14. root /root/website/;
    15. }
    16. # 所有动态请求都转发给tomcat处理
    17. location ~ \.(jsp|do)$ {
    18. proxy_pass http://dynamic_server;
    19. }
    20. error_page 500 502 503 504 /50x.html;
    21. location = /50x.html {
    22. root /root/website/;
    23. }
    24. }


    知识链接

  6. Nginx架构原理科普