Nginx介绍

Nginx是什么?

Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。

其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。

  • 正向代理

  • 反向代理

正向代理的 Proxy 是多对一,反向代理的 Proxy 是一对多。

反向代理是代理服务端,对外暴露反向代理服务器的IP,不暴露真实服务器的IP,客户端发送请求到反向代理服务器,反向代理服务器自己去找真正的服务端。

Nginx的优点

  • 速度更快、并发更高速度更快、并发更高

单次请求或者高并发请求的环境下,Nginx 都会比其他 Web 服务器响应的速度更快。一方面在正常情况下,单次请求会得到更快的响应,另一方面,在高峰期(如有数以万计的并发请求),Nginx 比其他 Web 服务器更快的响应请求。Nginx 之所以有这么高的并发处理能力和这么好的性能原因在于 Nginx 采用了多进程和 I/O 多路复用(epoll)的底层实现。

  • 配置简单,扩展性强

Nginx 的设计极具扩展性,它本身就是由很多模块组成,这些模块的使用可以通过配置文件的配置来添加。这些模块有官方提供的也有第三方提供的模块,如果需要完全可以开发服务自己业务特性的定制模块

  • 高可靠性

Nginx 采用的是多进程模式运行,其中有一个 master 主进程和 N 多个 worker 进程,一个 master 管理多个 worker,worker 进程的数量我们可以手动设置,每个 worker 进程之间都是相互独立提供服务,并且 master 主进程可以在某一个 worker 进程出错时,快速去「拉起」新的 worker 进程提供服务。

image

  • 热部署(核心)

现在互联网项目都要求以 7 * 24 小时进行服务的提供,针对于这一要求,Nginx 也提供了热部署功能,即可以在 Nginx 不停止的情况下,对 Nginx 进行文件升级、更新配置和更换日志文件等功能。

  • 成本低、BSD许可证

BSD 是一个开源的许可证,世界上的开源许可证有很多,现在比较流行的有六种分别是 GPL、BSD、MIT、Mozilla、Apache、LGPL。

Nginx 提供的基本功能服务从大体上归纳为「基本 HTTP 服务」、「高级 HTTP 服务」和「邮件服务」等三大类。

常用命令

选项 作用
-? 和 -h 显示帮助信息
-v 打印版本号信息并退出
-V 打印版本号信息和配置信息并退出
-t 测试 Nginx 的配置文件语法是否正确并退出
-T 测试 Nginx 的配置文件语法是否正确并列出用到的配置文件信息然后退出
-q 在配置测试期间过滤掉非错误消息
-s signal 信号,后面的命令和服务信号控制功能类似: stop:快速关闭,类似于 TERM/INT 信号的作用 quit:优雅的关闭,类似于 QUIT 信号的作用 reopen:重新打开日志文件类似于 USR1 信号的作用 reload:重启 Nginx,类似于 HUP 信号的作用
-p prefix,指定 Nginx 的默认安装路径,(默认为:/usr/local/nginx/)
-c filename,指定 Nginx 的配置文件路径,(默认为:conf/nginx.conf)
-g 用来补充 Nginx 配置文件,向 Nginx 服务指定启动时应用全局的配置

配置文件

Nginx配置文件的位置:/usr/local/nginx/conf/nginx.conf。
将Nginx的配置文件分为三部分:全局块、events块、http块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
worker_processes  1;   # 指令用于配置 Nginx 生成 worker 工作进程的数量,这个是 Nginx 服务器实现并发处理服务的关键所在。 

events { # 这是 events 块
worker_connections 1024;
}

http { # 这是 http 块
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;

server {
listen 80; # 监听 80 端口
server_name localhost; # 监听请求过来的 IP
location / { # 请求的地址是 /,则进入这个配置,访问 idnex.html
root html; # 进入 html 目录找到访问的页面
index index.html index.htm;
}
# 如果访问的页面是 500 502 503 504,则发送 /50x.html 请求
error_page 500 502 503 504 /50x.html;
location = /50x.html { # 如果匹配上 /50x.html 请求
root html; # 则进入 html 目录找到 /50x.html
}
}
}

全局块

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

events块

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

  • events指令

    accept_mutex 指令用来设置是否开启 Nginx 网络连接序列化。默认开启。

语法 默认值 位置
accept_mutex <on | off>; accept_mutex on; events

这个配置主要可以用来解决常说的「惊群」问题。大致意思是在某一个时刻,客户端发来一个请求连接,Nginx 后台是以多进程的工作模式,也就是说有多个 worker 进程会被同时唤醒,但是最终只会有一个进程可以获取到连接,如果每次唤醒的进程数目太多,就会影响 Nginx 的整体性能。如果将上述值设置为 on (开启状态),将会对多个 Nginx 进程接收连接进行序列号,一个个来唤醒接收,就防止了多个进程对连接的争抢。

如图的小狗,如果只是一块「骨头」出现,则只需要唤醒一个小狗即可(开启 on),如果多个「骨头」如三个同时出现,那么唤醒三个小狗效率更高(此时需要设置 off)

http块

http块是Nginx中使用最频繁的部分。http块包括http全局块和server块。

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

    • 我们都知道浏览器中可以显示的内容有 HTML、XML、GIF 等种类繁多的文件、媒体等资源,浏览器为了区分这些资源,就需要使用 MIME Type。
    • 所以说 MIME Type 是网络资源的媒体类型。Nginx 作为 Web 服务器,也需要能够识别前端请求的资源类型。
  • server块和虚拟主机有密切关系,每个http块可以包含多个server块,每个server块相当于一个虚拟主机。每个server块又包含server全局块、多个location块。

    • server全局块用于虚拟机监听配置,虚拟主机名称和IP配置。
    • loaction块基于Nginx服务器接收到的请求路径,对虚拟主机以外的路径进行匹配,对特定请求进行特定处理

静态资源部署与访问

  • 静态资源与动态资源

上网去搜索访问资源对于我们来说并不陌生,通过浏览器发送一个 HTTP 请求实现从客户端发送请求到服务器端获取所需要内容后并把内容回显展示在页面的一个过程。这个时候,我们所请求的内容就分为两种类型,一类是静态资源、一类是动态资源。

静态资源即指在服务器端真实存在并且能直接拿来展示的一些文件,比如常见的 html 页面、css 文件、js 文件、图片、视频等资源;

动态资源即指在服务器端真实存在但是要想获取需要经过一定的业务逻辑处理,根据不同的条件展示在页面不同这 一部分内容,比如说报表数据展示、根据当前登录用户展示相关具体数据等资源;

  • 静态资源优化

    • ```nginx
      sendfile on;#该指令是用来开启高效的文件传输模式。默认关闭,建议开启。
      tcp_nopush on;#该指令必须在 sendfile 打开的状态下才会生效,主要是用来提升网络包的传输「效率」。默认关闭。
      tcp_nodeplay on;#该指令必须在 keep-alive 连接开启的情况下才生效,来提高网络包传输的「实时性」。默认开启。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33

      - > 经过分析,『 tcp_nopush 』和『 tcp_nodelay 』看起来是「互斥的」,那么为什么要将这两个值都打开?
      >
      > 需要知道的是在 Linux2.5.9 以后的版本中两者是可以兼容的
      >
      > - 『 tcp_nopush 』开启可以确保在发送到客户端之前数据包已经充分「填满」,这大大减少了网络开销,并加快了文件发送的速度。
      >
      > - 当它到达最后一个可能因为没有「填满」而暂停的数据包时,Nginx 会忽略『 tcp_nopush 』参数, 『 tcp_nodelay 』强制套接字发送数据。
      >
      > 由此可知,『 tcp_nopush 』可以与『 tcp_nodelay 』一起设置,它比单独配置『 tcp_nodelay 』具有更强的性能。

      - **静态资源压缩**

      - 我们传送一个 1M 的数据和一个 10M 的数据那个效率高?答案显而易见,**传输内容小,速度就会快**。那么问题又来了,同样的内容,如果把大小降下来,我们脑袋里面要蹦出一个词就是「压缩」
      - 在 Nginx 的配置文件中可以通过配置 gzip 来对静态资源进行压缩,相关的指令可以配置在 http 块、server 块和 location 块中,Nginx 可以通过对这些指令进行解析和处理:
      - `ngx_http_gzip_module` 模块
      - `ngx_http_gzip_static_module` 模块
      - `ngx_http_gunzip_module` 模块

      - **静态资源缓存**

      - 当浏览器请求 Nginx 服务器的资源后,我们可以让这些资源缓存在浏览器里,这样再一次请求相同的资源时,无需请求 Nginx 服务器,直接从浏览器的缓存里获取,减少 Nginx 服务器的压力。

      - `expires`指令用来控制页面缓存的作用。可以通过该指令控制 HTTP 应答中的『Expires』和『Cache-Control』

      - 可在配置文件进行配置

      - ```nginx
      location ~ .*\.(html|js|css|png|jpg|jpeg|gif)$ {
      # ...
      expires max
      # ...
      }
      • ```nginx
        expires 30s; # 表示把数据缓存 30 秒

        expires 30m; # 表示把数据缓存 30 分

        expires 10h; # 表示把数据缓存 10 小时

        expires 1d; # 表示把数据缓存 1 天

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39

        # 反向代理

        ## proxy_pass

        该指令用来设置被代理服务器地址,可以是主机名称、IP 地址加端口号形式,没有默认值。

        | 语法 | 默认值 | 位置 |
        | ----------------- | ------ | -------- |
        | proxy_pass <URL>; | — | location |

        `URL`:为要设置的被代理服务器地址,包含传输协议(`http`、`https://`)、主机名称或 IP 地址加端口号

        准备两台服务器或者按照 Linux 系统的虚拟机,这里是 `192.168.200.133` 和 `192.168.200.146`,为了方便,我们称前者为服务器 A,后者为服务器 B。

        ```nginx
        server {
        listen 80;
        server_name localhost;
        location / {
        # 下面两个地址加不加斜杠,效果都一样,因为 location 后的 / 会添加在代理地址后面
        proxy_pass http://192.168.200.146;
        proxy_pass http://192.168.200.146/;
        }
        }
        # 当客户端请求服务器 A http://192.168.200.133,它会转发给服务器 B,此时的服务器 A 就是一个代理的角色。

        server{
        listen 80;
        server_name localhost;
        location /server {
        # 下面两个地址必须加斜杠,因为 location 后的 /server 会添加在代理地址后面,第一个将没有 / 结尾
        #proxy_pass http://192.168.200.146;
        proxy_pass http://192.168.200.146/;
        }
        }
        # 上面的 location:当客户端访问 http://localhost/server/index.html
        # 第一个 proxy_pass 就变成了 http://localhost/server/index.html
        # 第二个 proxy_pass 就变成了 http://localhost/index.html 效果就不一样了。

如果不以 / 结尾,则 location 后的 /server 会添加在地址后面,所以第一个 proxy_pass 因为没有 / 结尾而被加上 /server

而第二个自带了 / ,所以不会添加 /server

上面的例子仅仅针对:访问任意请求如 /server 时,想要代理到其他服务器的首页,则加 /

否则你如果真的想访问 /server 下的资源,那么不要加 /

proxy_set_header

该指令可以更改 Nginx 服务器接收到的客户端请求的请求头信息,然后将新的请求头发送给代理的服务器。默认值是发送代理服务器的地址和 close。

服务器A配置:

1
2
3
4
5
6
7
8
server {
listen 8080;
server_name localhost;
location /server { # 访问 /server 触发代理
proxy_pass http://192.168.200.146:8080/; # 配置服务器 B 的地址
proxy_set_header username TOM; # 发送 key 为 username,value 为 TOM 的请求头给服务器 B
}
}

服务器B配置:

1
2
3
4
5
6
7
server {
listen 8080;
server_name localhost;
default_type text/plain;
return 200 $http_username; # 获取代理服务器发送过来的 http 请求头的 username 值
}

测试结果:

proxy_redirect

该指令是用来重置头信息中的『 Location 』和『 Refresh 』的值,防止客户端可以看到被代理服务器的地址

  • 为什么要使用该指令?

首先说明一下思路:客户端通过代理服务器 A 访问服务器 B 的资源,但是服务器 B 不存在该资源,则会报错。此时我们不希望它直接返回报错页面给客户端,我们希望服务器 B 返回的是它的欢迎页面。那么如何做呢?

  • 首先在服务器 B 进行判断是否存在资源,不存在则返回自己的欢迎页面,即重定向到自己的欢迎页面地址并返回,此时浏览器的地址将会发生改变
  • 代理服务器 A 收到服务器 B 的欢迎页面和地址,但是我们不能直接返回给客户端,因为它会暴露服务器 B 的地址,这是重定向的原因

  • 此时用到 proxy_redirect 指令,重置服务器 B 返回过来的『 Location 』和『 Refresh 』值,将两个值改为代理服务器 A 的某个地址
  • 因为改为了代理服务器 A 的某个地址,所以代理服务器 A 根据这个地址又去获取理服务器 B 的欢迎页面地址,返回给客户端

总结:

  1. 客户端通过 A 找 B 不存在的资源,B 不想返回报错页面,于是重定向到自己的欢迎页面地址并返回给 A。
  2. A 收到了页面和地址(正常情况不要接收地址,只接收页面),发现不能暴露 B 的地址,于是修改接收的 B 的地址为自己的某一个地址。
  3. 这个地址会重新发送请求去获取 B 的欢迎页面地址,然后返回给客户端。

服务器B:

1
2
3
4
5
6
7
server {
listen 8081;
server_name localhost;
if (!-f $request_filename){
return 302 http://192.168.200.146; # 2.如果请求的资源不存在,则重定向到服务器 B
}
}

服务器A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 8081;
server_name localhost;
location / {
proxy_pass http://192.168.200.146:8081/; # 1.转发给服务器 B
proxy_redirect http://192.168.200.146 http://192.168.200.133; # 3.修改服务器 B 的地址
}
}
# 该 server 去请求服务器 B 的欢迎页面
server {
listen 80;
server_name 192.168.200.133;
location / {
proxy_pass http://192.168.200.146; # 4.重新发送请求给服务器 B,获取欢迎页面
}
}

实战

  • 当三台服务器内容不相同时
  • 服务器有限,只能以三个端口模拟三台服务器,实际上是一个 IP 对应一个服务器

代理服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 代理服务器
server {
listen 8082;
server_name localhost;
location /server1 {
proxy_pass http://192.168.200.146:9001/; # 代理 server1
}
location /server2 {
proxy_pass http://192.168.200.146:9002/; # 代理 server2
}
location /server3 {
proxy_pass http://192.168.200.146:9003/; # 代理 server3
}
}

服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 服务器
# server1
server {
listen 9001;
server_name localhost;
default_type text/html;
return 200 '<h1>192.168.200.146:9001</h1>'
}
# server2
server {
listen 9002;
server_name localhost;
default_type text/html;
return 200 '<h1>192.168.200.146:9002</h1>'
}
# server3
server {
listen 9003;
server_name localhost;
default_type text/html;
return 200 '<h1>192.168.200.146:9003</h1>'
}

均衡负载

实例

准备四台服务器,一台用来做负载均衡器,三台用来接收负载均衡器的请求。

image

因为目前只有两台服务器,所以一台用来做负载均衡器,另外一台用来接收负载均衡器的请求。

服务器设置:这里以三个端口代替三个服务器,在配置文件进行如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 服务器 1
server {
listen 9001;
server_name localhost;
default_type text/html;
location /{
return 200 '<h1>192.168.200.146:9001</h1>';
}
}
# 服务器 2
server {
listen 9002;
server_name localhost;
default_type text/html;
location /{
return 200 '<h1>192.168.200.146:9002</h1>';
}
}
# 服务器 3
server {
listen 9003;
server_name localhost;
default_type text/html;
location / {
return 200 '<h1>192.168.200.146:9003</h1>';
}
}

负载均衡器设置:这是一个 Nginx 代理服务器,让它去负载均衡访问三个服务器,在配置文件进行如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
upstream backend{
server 192.168.200.146:9091;
server 192.168.200.146:9092;
server 192.168.200.146:9093;
}
server {
listen 8083;
server_name localhost;
location / {
proxy_pass http://backend; # backend 要对应上 upstream 后的值,根据需求修改
}
}

访问负载均衡器的地址,如 http://192.168.200.133:8083,它会找到 proxy_pass 后的地址,比如上方,它会根据 backend 找到对应的 upstream 里内地址,替换掉 backend,变成:

  • proxy_pass http://192.168.200.146:9091
  • proxy_pass http://192.168.200.146:9092
  • proxy_pass http://192.168.200.146:9093

但是它不会全部访问三个服务器地址,而是根据自己的算法(轮询)选择其中一个服务器地址。

状态

代理服务器在负责均衡调度中的状态有以下几个:

状态 概述
down 当前的 server 暂时不参与负载均衡
backup 预留的备份服务器
max_fails 指令设置允许请求代理服务器失败的次数,默认为 1。
fail_timeout 指令设置经过 max_fails 失败后,服务暂停的时间,默认是 10 秒。
max_conns 限制最大的接收连接数
1
2
3
4
5
6
7
8
9
10
11
12
upstream backend{
server 192.168.200.133:9001 down;//该服务不可用
server 192.168.200.133:9002 backup;//当主服务器挂掉才使用
server 192.168.200.133:9003 max_fails=3 fail_timeout=15;
}
server {
listen 8083;
server_name localhost;
location /{
proxy_pass http://backend;
}
}

均衡负载策略

介绍完 Nginx 负载均衡的相关指令后,我们已经能实现将用户的请求分发到不同的服务器上,那么除了采用默认的分配方式以外,我们还能采用什么样的负载算法?

Nginx 的 upstream 支持如下六种方式的分配算法,分别是:

算法名称 说明
轮询 默认方式
weight 权重方式
ip_hash 依据 IP 分配方式
least_conn 依据最少连接方式
url_hash 依据 URL 分配方式
fair 依据响应时间方式
  • 轮询:这是 upstream 模块负载均衡默认的策略。每个请求会按时间顺序逐个分配到不同的后端服务器。轮询不需要额外的配置。

    • ```nginx
      upstream backend{
      server 192.168.200.146:9001;
      server 192.168.200.146:9002;
      server 192.168.200.146:9003;
      }
      server {
      listen 8083;
      server_name localhost;
      location /{
      proxy_pass http://backend;
      }
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17

      - weight:`weight` 指令用来设置服务器的权重,默认为 1,权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器硬件配置进行调整的,所有此策略比较适合服务器的硬件配置差别比较大的情况。

      - ```nginx
      upstream backend{
      server 192.168.200.146:9001 weight=10;
      server 192.168.200.146:9002 weight=5;
      server 192.168.200.146:9003 weight=3;
      }
      server {
      listen 8083;
      server_name localhost;
      location /{
      proxy_pass http://backend;
      }
      }

  • ip_hash: 当ip_hash 指令能够将某个客户端 IP 的请求通过哈希算法定位到同一台后端服务器上。这样,当来自某一个 IP 的用户在后端 Web 服务器 A 上登录后,在访问该站点的其他 URL,能保证其访问的还是后端 Web 服务器 A

    总结:哪个服务器曾经处理过请求,无论在哪里,相同的请求依然让该服务器处理

    • image
  • least_conn: 最少连接数,把请求转发给连接数较少的后端服务器。

  • url_hash: 按访问 URL 的 hash 结果来分配请求,使每个 URL 定向到同一个后端服务器,要配合缓存命中来使用。

  • fair: fair 指令采用的不是内建负载均衡使用的轮换的均衡算法,而是可以根据页面大小、加载时间长短智能的进行负载均衡。

    • 但是这样直接使用会报错,因为 fair 属于第三方模块实现的负载均衡。需要添加 nginx-upstream-fair 模块
1
2
3
4
5
6
7
8
9
10
11
12
13
upstream backend{
ip_hash;#//least_conn;//hash &request_uri;//fair;
server 192.168.200.146:9001;
server 192.168.200.146:9002;
server 192.168.200.146:9003;
}
server {
listen 8083;
server_name localhost;
location /{
proxy_pass http://backend;
}
}

缓存集成

  • Nginx 作为 Web 缓存服务器,它介于客户端和应用服务器之间,当用户通过浏览器访问一个 URL 时,Web 缓存服务器会去应用服务器获取要展示给用户的内容,将内容缓存到自己的服务器上,当下一次请求到来时,如果访问的是同一个 URL,Web 缓存服务器就会直接将之前缓存的内容返回给客户端,而不是向应用服务器再次发送请求。Web 缓存降低了应用服务器、数据库的负载,减少了网络延迟,提高了用户访问的响应速度,增强了用户的体验。

  • Nginx 是从 0.7.48 版开始提供缓存功能。Nginx 是基于 Proxy Store 来实现的,其原理是把 URL 及相关组合当做 Key,在使用 MD5 算法对 Key 进行哈希化,得到硬盘上对应的哈希目录路径,从而将缓存内容保存在该目录中。它可以支持任意 URL 连接,同时也支持 404/301/302 这样的非200 状态码。Nginx 即可以支持对指定 URL 或者状态码设置过期时间,也可以使用 purge 命令来手动清除指定 URL 的缓存。

image

  • Nginx 的 Web 缓存服务主要是使用 ngx_http_proxy_module 模块相关指令集来完成