Loading

[06] Nginx_1

1. Nginx 概述

1.1 Nginx 说明

https://lnmp.org/nginx.html

Nginx ("engine x") 是一个高性能的 HTTP 和反向代理服务器,同时也是一个 IMAP/POP3/SMTP 代理服务器。特点是占有内存少,并发能力强,事实上 Nginx 的并发能力确实在同类型的网页服务器中表现较好。

Nginx 以“事件驱动”的方式编写,所以有非常好的性能,同时也是一个非常高效的反向代理、负载平衡。其拥有匹配 Lighttpd 的性能,同时还没有 Lighttpd 的内存泄漏问题,而且 Lighttpd 的 mod_proxy 也有一些问题并且很久没有更新。

Nginx 做为 HTTP 服务器,有以下几项基本特性:

  • 处理静态文件,索引文件以及自动索引;打开文件描述符缓冲。
  • 无缓存的反向代理加速,简单的负载均衡和容错。
  • FastCGI,简单的负载均衡和容错。
  • 模块化的结构。包括 gzipping,byte ranges,chunked responses,以及 SSI-filter 等 filter。如果由 FastCGI 或其它代理服务器处理单页中存在的多个 SSI,则这项处理可以并行运行,而不需要相互等待。
  • 支持 SSL 和 TLSSNI。

Nginx 可以作为静态页面的 web 服务器,同时还支持 CGI 协议的动态语言,比如 perl、php 等。但是不支持 Java(Java 程序只能通过与 tomcat 配合完成)。

Nginx 专为性能优化而开发,性能是其最重要的考量,实现上非常注重效率。它支持内核 poll 模型,能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数。Nginx 具有很高的稳定性。其它 HTTP 服务器,当遇到访问的峰值,或者有人恶意发起慢速连接时,也很可能会导致服务器物理内存耗尽频繁交换,失去响应,只能重启服务器。例如当前 apache 一旦上到 200 个以上进程,web 响应速度就明显非常缓慢了。而 Nginx 采取了分阶段资源分配技术,使得它的 CPU 与内存占用率非常低。Nginx 官方表示保持 10,000 个没有活动的连接,它只占 2.5M 内存,所以类似 DOS 这样的攻击对 Nginx 来说基本上是毫无用处的。就稳定性而言,Nginx 比 lighthttpd 更胜一筹。

Nginx 支持热部署。它的启动特别容易, 并且几乎可以做到 7*24 不间断运行,即使运行数个月也不需要重新启动。你还能够在不间断服务的情况下,对软件版本进行进行升级。

Nginx 采用 master-slave 模型,能够充分利用 SMP 的优势,且能够减少工作进程在磁盘 I/O 的阻塞延迟。当采用 select()/poll() 调用时,还可以限制每个进程的连接数。

1.2 相关概念

(1)正向代理

Nginx 不仅可以做反向代理,实现负载均衡。还能用作正向代理来进行上网等功能。

如果把局域网外的 Internet 想象成一个巨大的资源库,则局域网中的客户端要访问 Internet,则需要通过代理服务器来访问,这种代理服务就称为"正向代理"。

(2)反向代理

反向代理,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,再返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器 IP 地址。

(3)负载均衡

客户端发送多个请求到服务器,服务器处理请求,有一些可能要与数据库进行交互,服务器处理完毕后,再将结果返回给客户端。

这种架构模式对于早期的系统相对单一,并发请求相对较少的情况下是比较适合的,成本也低。但是随着信息数量的不断增长,访问量和数据量的飞速增长,以及系统业务的复杂度增加,这种架构会造成服务器相应客户端的请求日益缓慢,并发量特别大的时候,还容易造成服务器直接崩溃。很明显这是由于服务器性能的瓶颈造成的问题,那么如何解决这种情况呢?

我们首先想到的可能是升级服务器的配置,比如提高 CPU 执行频率,加大内存等提高机器的物理性能来解决此问题,但是我们知道摩尔定律的日益失效,硬件的性能提升已经不能满足日益提升的需求了。最明显的一个例子,天猫双十一当天,某个热销商品的瞬时访问量是极其庞大的,那么类似上面的系统架构,将机器都增加到现有的顶级物理配置,都是不能够满足需求的。那么怎么办呢?

上面的分析我们去掉了增加服务器物理配置来解决问题的办法,也就是说纵向解决问题的办法行不通了,那么横向增加服务器的数量呢?这时候集群的概念产生了,单个服务器解决不了,我们增加服务器的数量,然后将请求分发到各个服务器上,将原先请求集中到单个服务器上的情况改为将请求分发到多个服务器上,将负载分发到不同的服务器,也就是我们所说的 "负载均衡"。

(4)动静分离

为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度,降低原来单个服务器的压力。

1.3 Nginx 安装

(1)所需安装包

  • pcre-8.37.tar.gz
  • openssl-1.0.1t.tar.gz
  • zlib-1.2.8.tar.gz
  • nginx-1.12.2.tar.gz

(2)逐个安装

  • 安装 pcre 依赖
    tar -xvf pcre-8.37.tar.gz
    cd pcre-8.37/
    ./configure
    make && make install
    pcre-config --version [查看版本]
    
  • 安装 openssl 、zlib 、 gcc 依赖
    yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel
    
  • 安装 nginx
    tar -xvf nginx-1.12.2.tar.gz
    cd nginx-1.12.2/
    ./configure
    make && make install
    

(3)有关命令:均要在 /usr/local/nginx/sbin 目录下执行

  • 【启动】./nginx
  • 【关闭】./nginx -s stop
  • 【重启】./nginx -s reload
  • 【查看版本】./nginx -v

(4)访问测试

启动之后,通过 Win 浏览器访问报 404。

  1. 先确认 nginx 配置是否 OK
  2. 确认网络是否可达。根据报错原因,继而又通过 netstat -anp | more 查看端口监听情况,nginx 确实在监听 80 端口。
  3. 是否受防火墙安全控制 ← 404 原因!
  4. 排除以上原因之后,远程实际再测试。

2. 配置文件

2.1 文件所在位置

2.2 文件内容结构

#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;

    sendfile        on;

    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

根据上述文件,我们可以很明显的将 nginx.conf 配置文件分为 3 部分。

2.3 全局块

从配置文件开始到 events 块之间的内容,主要会设置一些影响 Nginx 服务器整体运行的配置指令,主要包括配置运行 Nginx 服务器的用户(组)、允许生成的 worker process 数,进程 PID 存放路径、日志存放路径和类型以及配置文件的引入等。

比如上面第一行配置的:worker_processes 1; 这是 Nginx 服务器并发处理服务的关键配置,worker_processes 值越大,可以支持的并发处理量也越多,但是会受到硬件、软件等设备的制约。

2.4 events 块

events 块涉及的指令主要影响 Nginx 服务器与用户的网络连接,常用的设置包括是否开启对多 work process 下的网络连接进行序列化,是否允许同时接收多个网络连接,选取哪种事件驱动模型来处理连接请求,每个 word process 可以同时支持的最大连接数等。

上述例子就表示每个 work process 支持的最大连接数为 1024,这部分的配置对 Nginx 的性能影响较大,在实际中应该灵活配置。

2.5 http 块

这算是 Nginx 服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。

需要注意的是:http 块也可以包括 http 全局块、server 块。

a. http 全局块

http 全局块配置的指令包括文件引入、MIME-TYPE 定义、日志自定义、连接超时时间、单链接请求数上限等。

b. server 块

这块和虚拟主机有密切关系,虚拟主机从用户角度看,和一台独立的硬件主机是完全一样的,该技术的产生是为了节省互联网服务器硬件成本。

每个 http 块可以包括多个 server 块,而每个 server 块就相当于一个虚拟主机;而每个 server 块也分为全局 server 块,以及可以同时包含多个 locaton 块。

【全局 server 块】最常见的配置是本虚拟机主机的监听配置和本虚拟主机的名称或 IP 配置。

【location 块】一个 server 块可以配置多个 location 块。这块的主要作用是基于 Nginx 服务器接收到的请求字符串(例如 server_name/uri-string),对虚拟主机名称(也可以是 IP 别名)之外的字符串(例如前面的 /uri-string)进行匹配,对特定的请求进行处理。地址定向、数据缓存和应答控制等功能,还有许多第三方模块的配置也在这里进行。

c. location 指令

该指令用于匹配 URL,其语法规则: location [=|~|~*|^~] /uri/ { ... }

  1. `` 空能够匹配以需要匹配的路径为前缀的 uri
  2. = 用于不含正则表达式的 uri 前,要求请求字符串与 uri 严格匹配。如果匹配成功,就停止继续向下搜索并立即处理该请求。
  3. ~ 用于表示 uri 包含正则表达式,并且区分大小写。
  4. ~* 用于表示 uri 包含正则表达式,并且不区分大小写。
  5. ^~ 用于不含正则表达式的 uri 前,要求 Nginx 服务器找到标识 uri 和请求字符串匹配度最高的 location 后,立即使用此 location 处理请求,而不再使用 location 块中的正则 uri 和请求字符串做匹配。

注意:如果 uri 包含正则表达式,则必须要有 ~ 或者 ~* 标识。

3. 原理说明

3.1 工作模式

3.2 相关问题

1. Master-Workers 的机制的好处

首先,对于每个 worker 进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。

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

当然,worker 进程的异常退出,肯定是程序有 bug 了,异常退出会导致当前 worker 上的所有请求失败,不过不会影响到所有请求,所以降低了风险。

2. 需要设置多少个 worker?

Nginx 同 Redis 类似都采用了 IO 多路复用机制,每个 worker 都是一个独立的进程,但每个进程里只有一个主线程,通过异步非阻塞的方式来处理请求, 即使是千上万个请求也不在话下。每个 worker 的线程可以把一个 CPU 的性能发挥到极致。所以 worker 数和服务器的 CPU 数相等是最为适宜的。设少了会浪费 CPU,设多了会造成 CPU 频繁切换上下文带来的损耗。

3. 连接数 worker_connection

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

当然,这里说的是最大连接数,对于 HTTP 请求本地资源来说 , 能够支持的最大并发数量是 worker_connections * worker_processes;如果是支持 HTTP 1.1 的浏览器每次访问要占两个连接,所以普通的静态访问最大并发数是: worker_connections * worker_processes / 2

而如果是 HTTP 作为反向代理来说,最大并发数量应该是 worker_connections * worker_processes / 4。因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,也会占用 2 个连接。

3.3 惊群问题

摘自:https://blog.csdn.net/qq_34556414/article/details/107079781

“惊群”问题,即多个 worker 子进程监听相同端口时,在 accept 建立新连接时会有争抢,引发不必要的上下文切换,增加系统开销。

简而言之,惊群现象就是当多个进程/线程等待同一个事件,如果这个事件发生,会唤醒所有的进程/线程,但最终只可能有一个进程/线程能对该事件进行处理,其他进程/线程会在获取事件失败后重新休眠。

惊群通常发生在网络服务器上。父进程首先绑定一个端口监听 socket,然后 fork 出多个子进程,子进程们开始循环等待处理(e.g. accept)这个 socket。每当用户发起一个 TCP 连接时,多个子进程同时被唤醒,然后其中一个子进程 accept 新连接成功,余者皆失败,重新休眠。

惊群现象非常像把食物丢进鸡群,引起所有的鸡一起哄抢食物。如果这个食物只是一粒米的话,唤醒所有的鸡一起来抢的话则非常没有必要。

如何解决惊群问题呢?

解决的惊群的方法也很简单,每次把进程(鸡)排个序,来了新的请求(食物)只唤醒排在第一位的就好。其实早在 Linux2.6,accept 系统调用的惊群问题已经被解决了。

但我们的网络程序不会单独阻塞在 accept 调用上,我们还有许多其他网络事件要处理。在 accept 之前,我们依然还得处理惊群现象。拿 Nginx 来说,Master 进程监听端口号,所有的 Worker 进程开始用 epoll_wait 来处理新事件(使用 epoll 模型),如果不加任何保护,一个新连接来临时,会有多个 Worker 进程在 epoll_wait 后被唤醒。也就是说,我们还要解决 epoll 的惊群问题。


If accept_mutex is enabled, worker processes will accept new connections by turn. Otherwise, all worker processes will be notified about new connections, and if volume of new connections is low, some of the worker processes may just waste system resources.

如果 accept_mutex 启用(NGINX 1.11.3 和更高版本以及 NGINX Plus R10 和更高版本中的默认设置),则工作进程将依次接受新的连接。否则,将通知所有 worker 进程有关新连接的信息,如果新连接的数量很少,则某些 worker 进程可能会浪费系统资源。

We recommend keeping the default value (off) unless you have extensive knowledge of your app’s performance and the opportunity to test under a variety of conditions, but it can lead to inefficient use of system resources if the volume of new connections is low. Changing the value to on might be beneficial under some high loads.

我们建议保留默认值(off),除非您对应用程序的性能有广泛的了解,并且有机会在各种条件下进行测试,但是如果新连接的数量很少,则可能导致系统资源的低效使用。在某些高负载下将其值设置为 on 可能是有益的。


Nginx 解决惊群问题的配置是 accept_mute。在打开 accept_mutex 的情况下,只有调用 ngx_trylock_accept_mutex 方法获取锁后,当前的 worker 进程才会去试着监听 web 端口(只有一个子进程会将监听套接字添加到 epoll 中),接受新的 TCP 连接事件(Accept 事件)。这样当一个新的连接来到时,就只有一个 Worker 子进程会被唤醒了。

Linux4.5 以后的版本中增加了 EPOLLEXCLUSIVE 支持以解决 epoll 的惊群问题,Nginx 从 1.11.3 版本之后也增加了对 EPOLLEXCLUSIVE 的支持,转而由操作系统自己解决 epoll 的惊群,从此之后 accept_mutex 从默认的 on 变成了默认 off。惊群问题完美解决~

如果在处理新建连接事件的过程中,在监听套接口上又来了新的请求会怎么样?这没有关系,当前进程只处理已缓存的事件,新的请求将被阻塞在监听套接口上,而前面曾提到监听套接口是以 ET 方式加入到事件监控机制里的,所以等到下一轮被哪个进程争取到锁并加到事件监控机制里时才会触发而被抓取出来

【小结】

假设你养了 100 只小鸡,现在你有一粒粮食,那么有两种喂食方法:

  • 你把这粒粮食直接扔到小鸡中间,100 只小鸡一起上来抢,最终只有 1 只小鸡能得手,其它 99 只小鸡只能铩羽而归。这就相当于关闭了 accept_mutex
  • 你主动抓 1 只小鸡过来,把这粒粮食塞到它嘴里,其它 99 只小鸡对此浑然不知,该干嘛干嘛。这就相当于激活了 accept_mutex

可以看到此场景下,激活 accept_mutex 相对更好一些,让我们修改一下问题的场景,我不再只有一粒粮食,而是一盆粮食,怎么办?

此时如果仍然采用主动抓小鸡过来塞粮食的做法就太低效了,一盆粮食不知何年何月才能喂完,大家可以设想一下几十只小鸡排队等着喂食时那种翘首以盼的情景。此时更好的方法是把这盆粮食直接撒到小鸡中间,让它们自己去抢,虽然这可能会造成一定程度的混乱,但是整体的效率无疑大大增强了。

简单点说,Apache 动辄就会启动成百上千的进程,如果发生惊群问题的话,影响相对较大;但是对 Nginx 而言,一般来说,worker_processes 会设置成 CPU 个数,所以最多也就几十个,即便发生惊群问题的话,影响相对也较小。accept_mutex 关闭了,可能会引起一定程度的惊群问题,表现为上下文切换增多(sar -w)或者负载上升,但是如果你的网站访问量比较大,为了系统的吞吐量,我还是建议大家关闭它。

最后附上一张图:章亦春,OpenResty Inc. 创始人兼 CEO

4. 反向代理

4.1 示例一

(1)目标效果

打开浏览器,在浏览器地址栏输入地址 www.1101.com,跳转到 Liunx 系统 tomcat 主页。

(2)思路分析

(3)准备工作

  1. 进入 tomcat 的 bin 目录中,启动 tomcat 服务器(如果访问不了,参考 Nginx 的访问测试 —— 查看防火墙是否开放了 8080 端口)。
  2. 设置域名和 ip 的对应关系

(4)反向代理配置

也就是在 nginx.conf 中进行请求转发的配置

(5)测试效果

4.2 示例二

(1)实现效果

使用 nginx 反向代理,根据访问的路径跳转到不同端口的服务中,给定 nginx 监听端口为 9001。

访问 http://192.168.206.128:9001/aaa/ 直接跳转到 127.0.0.1:8080
访问 http://192.168.206.128:9001/bbb/ 直接跳转到 127.0.0.1:8081

(2)准备工作

准备两个 tomcat 服务器,一个 8080 端口,一个 8081 端口;并为两只猫各自添加一个 html 作为目标访问资源。

iptables 中放行这 3 个端口。

(3)反向代理配置

之前的 server {...} 不用删,直接将配置追加到 nginx.conf 中。

(4)测试效果

5. 负载均衡

5.1 分配策略

随着互联网信息的爆炸性增长,负载均衡(load balance) 已经不再是一个很陌生的话题,顾名思义,负载均衡即是将负载分摊到不同的服务单元,既保证服务的可用性,又保证响应足够快,给用户很好的体验。

快速增长的访问量和数据流量催生了各式各样的负载均衡产品,很多专业的负载均衡硬件提供了很好的功能,但却价格不菲,这使得负载均衡软件大受欢迎,Nginx 就是其中的一个,在 Linux 下有 Nginx、LVS、Haproxy 等等服务可以提供负载均衡服务,而且 Nginx 提供了几种分配方式(策略):

  1. 轮询[默认]:每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。
  2. weight(权重):默认为 1,权重越高被分配的客户端越多指定轮询几率,weight 和访问比率成正比,用于后端服务器性能不均的情况。
    upstream server_pool{
        server 192.168.206.128:8080 weight=6;
        server 192.168.206.128:8081 weight=7;
    }
    
  3. ip_hash:每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 的问题。
    upstream server_pool{
        ip_hash;
        server 192.168.206.128:8080;
        server 192.168.206.128:8081;
    }
    
  4. fair(第三方):按后端服务器的响应时间来分配请求,响应时间短的优先分配。
    upstream server_pool{
        server 192.168.206.128:8080;
        server 192.168.206.128:8081;
        fair;
    }
    

5.2 案例说明

(1)实现效果

浏览器地址栏输入地址:http://192.168.206.128/abc/index.html,负载均衡效果:平均到 8080 和 8081 端口中。

(2)准备工作

  1. 准备两台 tomcat 服务器,一台 8080,一台 8081。
  2. 在两台 tomcat 各自的 webapps 目录中,都创建名称为 abc 的文件夹,并在 abc 文件夹中创建页面 index.html,用于测试。

(3)使用默认策略

(4)测试效果

6. 动静分离

6.1 分离实现

Nginx 动静分离简单来说就是把动态跟静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。严格意义上说应该是动态请求跟静态请求分开,可以理解成使用 Nginx 处理静态页面,Tomcat 处理动态页面。

动静分离从目前实现角度来讲大致分为两种:一种是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案;另外一种方法就是动态跟静态文件混合在一起发布,通过 Nginx 来分开。

通过 location 指定不同的后缀名实现不同的请求转发。通过 expires 参数设置,可以使浏览器缓存过期时间,减少与服务器之前的请求和流量。具体 Expires 定义:是给一个资源设定一个过期时间,也就是说无需去服务端验证,直接通过浏览器自身确认是否过期即可,所以不会产生额外的流量。此种方法非常适合不经常变动的资源(如果经常更新的文件,不建议使用 Expires 来缓存)。我这里设置 3d,表示在这 3 天之内访问这个 URL,发送一个请求,比对服务器该文件最后更新时间没有变化,则不会从服务器抓取,返回状态码 304;如果有修改,则直接从服务器重新下载,返回状态码 200。

6.2 案例说明

(1)准备工作

在 Liunx 系统中准备静态资源,用于进行访问。

(2)Nginx 配置

(3)测试效果

(4)autoindex 效果

7. 高可用集群

7.1 Nginx 高可用

7.2 准备工作

  1. 需要两台 nginx 服务器: 192.168.17.129 和 192.168.17.131

  2. 使用命令 yum install keepalived –y 在两台服务器安装 keepalived

  3. 安装之后,再分别修改各自的 /etc/keepalived/keepalivec.conf 配置文件

    global_defs { # 全局配置
        notification_email {
            acassen@firewall.loc
            failover@firewall.loc
            sysadmin@firewall.loc
        }
        notification_email_from Alexandre.Cassen@firewall.loc
            smtp_server 192.168.17.129
            smtp_connect_timeout 30
            router_id LVS_DEVEL # 访问到主机,通过 vim /etc/host 查看主机名
    }
    
    vrrp_script chk_http_port { # 脚本配置
        script "/usr/local/src/nginx_check.sh"
        interval 2 # 检测脚本执行的间隔为 2s
        weight 2 # 当脚本条件成立,就改变当前主机的权重
    }
    
    vrrp_instance VI_1 { # 虚拟 IP 配置
        state BACKUP # 备份服务器上将 MASTER 改为 BACKUP
        interface ens33 // 网卡
        virtual_router_id 51 # 主、备机的 virtual_router_id 必须相同
        priority 90 # 主、备机取不同的优先级,主机值较大,备份机值较小
        advert_int 1 # 每隔 1s 发送心跳
        authentication { # 权限校验的方式
            auth_type PASS
            auth_pass 1111
        }
        virtual_ipaddress { # 可以绑定多个虚拟 IP
            192.168.17.50 // VRRP H 虚拟地址
        }
    }
    
  4. 在 /usr/local/src 添加检测脚本 nginx_check.sh

    #!/bin/bash
    A=`ps -C nginx –no-header |wc -l`
    if [ $A -eq 0 ];then
        /usr/local/nginx/sbin/nginx
        sleep 2
        if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then
            killall keepalived
        fi
    fi
    
  5. 把两台服务器上 [nginx] ./nginx 和 [keepalived] systemctl start keepalived.service 启动。

7.3 测试效果

1. 在浏览器地址栏输入虚拟 IP 地址:192.168.17.50

虚拟 IP 此时在主服务器上。

2. 把主服务器(192.168.17.129) 的 nginx 和 keepalived 停止,再输入 192.168.17.50,依旧可以访问(虚拟 IP 此时已绑定到了备份服务器上)。

posted @ 2020-11-14 21:53  tree6x7  阅读(122)  评论(0编辑  收藏  举报