Nginx

dev-env-resource

# 一、Nginx

# Nginx简介

Nginx官网 (opens new window)

Nginx官方中文文档 (opens new window)

Nginx的发音为 [ˈendʒɪnks]

它不仅是一个高性能的HTTP服务器反向代理服务器,同时它也是一个通用类型的代理服务器,支持绝大部分协议,如TCP、UDP、SMTP(电子邮件代理服务器)、HTTPS等。

俄罗斯的程序设计师伊戈尔·西索夫(Igor Sysoev)所开发,使用C语言编写 。

Nginx与Redis相同,都是基于多路复用模型构建出的产物,因此它与Redis同样具备资源占用少、并发支持高 的特点。

官方测试 Nginx 能够支支撑 5 万并发链接, 并且 cpu、内存等资源消耗却非常低,运行非常稳定。 在理论上单节点的Nginx同时支持5W并发连接,实际生产环境中,硬件基础到位再结合简单调优后确实能达到该数值。

# Nginx 应用场景

1、http 服务器

Nginx 是一个 http 服务可以独立提供 http 服务。可以做网页静态服务器

2、虚拟主机

可以实现在一台服务器虚拟出多个网站。例如个人网站使用的虚拟主机。

3、反向代理,负载均衡

当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用 Nginx 做反向代理。并且多台服务器可以平均分担负载,不会因为某台服务器负载高宕机而某台服务器闲置的情况。

# Nginx特点

  • 反向代理
  • 负载均衡
  • 限流

# 二、Nginx反向代理

# 代理

在Java设计模式中,代理模式是这样定义的:给某个对象提供一个代理对象,并由代理对象控制原对象的引用。

Nginx 主要能够代理几种协议,其中用到的最多的就是做Http代理服务器

# 正向代理

现在国内是访问不了 Google的,那么怎么才能访问 Google呢?如果我们电脑的对外公网 IP 地址能变成美国的 IP 地址,那不就可以访问 Google了。VPN 就是这样产生的。我们在访问 Google 时,先连上 VPN 服务器将我们的 IP 地址变成美国的 IP 地址,然后就可以顺利的访问了。

这里的 VPN 就是做正向代理的。正向代理服务器位于客户端和服务器之间,为了向服务器获取数据,客户端要向代理服务器发送一个请求,并指定目标服务器,代理服务器将目标服务器返回的数据转交给客户端。这里客户端是要进行一些正向代理的设置。

# 反向代理

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

理解这两种代理的关键在于代理服务器所代理的对象是什么,正向代理代理的是客户端,我们需要在客户端进行一些代理的设置。而反向代理代理的是服务器,作为客户端的我们是无法感知到服务器的真实存在的。

总结起来还是一句话:正向代理代理客户端,反向代理代理服务器。

# 指令说明

在 nginx.conf 配置文件中增加如下配置:

server {
    listen       80;
    server_name  www.123.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        index  index.html index.htm index.jsp;
    }
}
1
2
3
4
5
6
7
8
9

如上配置,我们监听80端口,访问域名为www.123.com,不加端口号时默认为80端口,故访问该域名时会跳转到127.0.0.1:8080路径上。

# 1)listen

listen指令用于配置网络监听。主要有如下三种配置语法结构:

  • 配置监听的IP地址
  • 配置监听端口
  • 配置 UNIX Domain Socket
listen *:80 | *:8080 #监听所有80端口和8080端口
listen  IP_address:port   #监听指定的地址和端口号
listen  IP_address     #监听指定ip地址所有端口
listen port     #监听该端口的所有IP连接
1
2
3
4

说明:

1、address:IP地址,如果是 IPV6地址,需要使用中括号[] 括起来,比如[fe80::1]等。

2、port:端口号,如果只定义了IP地址,没有定义端口号,那么就使用80端口。

# 2)server_name

server_name指令用于虚拟主机的配置。通常分为以下两种:

  1. 基于名称的虚拟主机配置

    语法格式如下:

    server_name   name ...;
    
    1

    ①、 对于name 来说,可以只有一个名称,也可以有多个名称,中间用空格隔开。而每个名字由两段或者三段组成,每段之间用“.”隔开。

    server_name 123.com www.123.com
    
    1

    ②、 可以使用通配符“*”,但通配符只能用在由三段字符组成的首段或者尾端,或者由两端字符组成的尾端。

    server_name *.123.com www.123.*
    
    1

    ③、还可以使用正则表达式,用“~”作为正则表达式字符串的开始标记。

    server_name ~^www\d+\.123\.com$;
    
    1

    该表达式“~”表示匹配正则表达式,以www开头(“^”表示开头),紧跟着一个0~9之间的数字,在紧跟“.123.co”,最后跟着“m”($表示结尾)

    以上匹配的顺序优先级如下:

    ①、准确匹配 server_name
    ②、通配符在开始时匹配 server_name
    ③、通配符在结尾时匹配 server_name
    ④、正则表达式匹配 server_name
    
    1
    2
    3
    4
  2. 基于 IP 地址的虚拟主机配置

    语法结构和基于域名匹配一样,而且不需要考虑通配符和正则表达式的问题。

    server_name 192.168.1.1
    
    1

# 3)location

location指令用于匹配 URL。语法如下:

location [ = | ~ | ~* | ^~] uri {

}
1
2
3

1、= :用于不含正则表达式的 uri 前,要求请求字符串与 uri 严格匹配,如果匹配成功,就停止继续向下搜索并立即处理该请求。

2、~:用于表示 uri 包含正则表达式,并且区分大小写。

3、~*:用于表示 uri 包含正则表达式,并且不区分大小写。

4、^~:用于不含正则表达式的 uri 前,要求 Nginx 服务器找到标识 uri 和请求字符串匹配度最高的 location 后,立即使用此 location 处理请求,而不再使用 location 块中的正则 uri 和请求字符串做匹配。

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

# 4)proxy_pass

proxy_pass指令用于设置被代理服务器的地址。可以是主机名称、IP地址加端口号的形式。

语法结构如下:

proxy_pass URL;
1

URL 为被代理服务器的地址,可以包含传输协议、主机名称或IP地址加端口号,URI等。

proxy_pass  http://www.123.com/uri;
1

# 5)index

index指令用于设置网站的默认首页。语法为:

index  filename ...;
1

后面的文件名称可以有多个,中间用空格隔开。

index  index.html index.jsp;
1

通常该指令有两个作用:

  1. 第一个是用户在请求访问网站时,请求地址可以不写首页名称;
  2. 第二个是可以对一个请求,根据请求内容而设置不同的首页。

# 三、Nginx负载均衡

# 单节点部署

早期的业务都是基于单体节点部署,由于前期访问流量不大,因此单体结构也可满足需求。

但随着业务增长,流量也越来越大,那么最终单台服务器受到的访问压力也会逐步增高。时间一长,单台服务器性能无法跟上业务增长,就会造成线上频繁宕机的现象发生,最终导致系统瘫痪无法继续处理用户的请求。

单体节点部署主要存在两个问题:

  1. 单体结构的部署方式无法承载日益增长的业务流量。
  2. 当后端节点宕机后,整个系统会陷入瘫痪,导致整个项目不可用。

因此在这种背景下,引入负载均衡技术势在必行。

# 负载均衡好处

  • 系统的高可用

    当某个节点宕机后可以迅速将流量转移至其他节点。

  • 系统的高性能

    多台服务器共同对外提供服务,为整个系统提供了更高规模的吞吐。

  • 系统的拓展性

    当业务再次出现增长或萎靡时,可再加入/减少节点,灵活伸缩。

# 负载均衡技术

主要有两种负载方案,硬件层面软件层面

  • 硬件层面,比较常用的硬件负载器有A10F5等。
  • 软件层面,如典型的Nginx等。

# Nginx负载均衡介绍

负载均衡的意思是在服务器集群中,需要有一台服务器作为调度者,客户端所有的请求都由调度者接收,调度者再根据每台服务器的负载情况,将请求分配给对应的服务器去处理;

在这个过程中,调度者如何合理分配任务,保证所有服务器将性能充分发挥,从而保持服务器集群的整体性能最优,这就是负载均衡的问题了。

Nginx是目前负载均衡技术中的主流方案,几乎绝大部分项目都会使用它。

# Nginx负载均衡的方式

# 1)轮询

轮询方式是Nginx负载默认的方式,顾名思义,所有请求都按照时间顺序分配到不同的服务上,如果服务Down掉,可以自动剔除,如下配置后轮训10001服务和10002服务。

upstream  dalaoyang-server {
       server    localhost:10001;
       server    localhost:10002;
}
1
2
3
4

# 2)权重

指定每个服务的权重比例,weight和访问比率成正比,通常用于后端服务机器性能不统一,将性能好的分配权重高来发挥服务器最大性能,如下配置后10002服务的访问比率会是10001服务的二倍。

upstream  dalaoyang-server {
       server    localhost:10001 weight=1;
       server    localhost:10002 weight=2;
}
1
2
3
4

# 3)iphash

每个请求都根据访问ip的hash结果分配,经过这样的处理,每个访客固定访问一个后端服务,如下配置(ip_hash可以和weight配合使用)。

upstream  dalaoyang-server {
       ip_hash; 
       server    localhost:10001 weight=1;
       server    localhost:10002 weight=2;
}
1
2
3
4
5

# 4)最少连接

将请求分配到连接数最少的服务上。

upstream  dalaoyang-server {
       least_conn;
       server    localhost:10001 weight=1;
       server    localhost:10002 weight=2;
}
1
2
3
4
5

# 5)fair

按后端服务器的响应时间来分配请求,响应时间短的优先分配。 需要插件来帮我们实现

upstream  dalaoyang-server {
       server    localhost:10001 weight=1;
       server    localhost:10002 weight=2;
       fair;  
}
1
2
3
4
5

# 四、Nginx限流熔断

HTTP Limit Zone 模块 (opens new window)是Nginx主要的一个基本模块,为我们提供了所需要的限流熔断功能。该模块可以针对条件,可以进行会话的并发连接数控制。例如:可以限制每个IP的并发连接数。

# 限流方式

# 1)令牌桶算法

令牌桶算法

算法思想是:

  • 令牌以固定速率产生,并缓存到令牌桶中;
  • 令牌桶放满时,多余的令牌被丢弃;
  • 请求要消耗等比例的令牌才能被处理;
  • 令牌不够时,请求被缓存。

# 2)漏桶算法

漏桶算法

算法思想是:

  • 水(请求)从上方倒入水桶,从水桶下方流出(被处理);
  • 来不及流出的水存在水桶中(缓存),以固定速率流出;
  • 水桶满后水溢出(丢弃)。
  • 这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃

相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列。这个桶是用来存放令牌的,队列才是用来存放请求的

# 区别

从作用上来说,漏桶和令牌桶算法最明显的区别就是是否允许突发流量(burst)的处理

  1. 漏桶算法能够强行限制数据的实时传输(处理)速率,对突发流量不做额外处理
  2. 令牌桶算法能够在限制数据的平均传输速率的同时,允许某种程度的突发传输

Nginx按请求速率限速模块使用的是漏桶算法,即能够强行保证请求的实时处理速度不会超过设置的阈值。

# 指令说明

  • [#limit_zone limit_zone]
  • [#limit_conn limit_conn]

# 1)limit_zone

语法: limit_zone zone_name $variable the_size

默认值: no

作用域: http

本指令定义了一个数据区,里面记录会话状态信息

  • $variable 定义判断会话的变量;

  • the_size 定义记录区的总容量。

例子:

limit_zone   my_name  $binary_remote_addr  10m;
1

定义一个叫“my_name”的记录区,总容量为 10M,以变量 $binary_remote_addr 作为会话的判断基准(即一个地址一个会话)。在这里使用的是 $binary_remote_addr 而不是 $remote_addr。

  • $remote_addr 的长度为 7 至 15 bytes,会话信息的长度为 32 或 64 bytes。
  • $binary_remote_addr 的长度为 4 bytes,会话信息的长度为 32 bytes。

当记录区的大小为 1M 的时候,大约可以记录 32000 个会话信息(一个会话占用 32 bytes)。

# 2)limit_conn

语法: limit_conn zone_name the_size

默认值: no

作用域: http, server, location

指定一个会话最大的并发连接数。 当超过指定的最发并发连接数时,服务器将返回 "Service unavailable" (503)。

例子:

limit_zone   my_name  $binary_remote_addr  10m;

server {
	location /download/ {
		limit_conn   my_name  1;
	}
}
1
2
3
4
5
6
7

定义一个叫“my_name”的记录区,总容量为 10M,以变量 $binary_remote_addr 作为会话的判断基准(即一个地址一个会话)。 限制 /download/ 目录下,一个会话只能进行一个连接。

简单点,就是限制 /download/ 目录下,一个IP只能发起一个连接,多过一个,一律503。

# AB测试

AB运行需要依赖apr-util包。

安装相关依赖包

#安装apr-util包
yum install apr-util
#安装依赖 yum-utils中的yumdownload 工具,如果没有找到 yumdownload 命令可以
yum install yum-utils
cd /opt
mkdir abtmp
cd abtmp
yum install yum-utils.noarch
yumdownloader httpd-tools*
rpm2cpio httpd-*.rpm | cpio -idmv
1
2
3
4
5
6
7
8
9
10

操作完成后,将会产生一个usr目录。ab文件就在这个usr目录中。

简单使用

./ab -c 100 -n 10000 http://127.0.0.1/index.html
1
  • -c 100 :每次并发100个;
  • -n 10000 : 共发送10000个请求;

# 五、Nginx实现动静分离

对于大多数使用者来说,Nginx作为一个静态文件服务器或者http请求转发器来使用。

它可以把静态文件的请求直接返回静态文件资源,把动态文件的请求转发给后台的处理程序。 Nginx的静态处理能力很强,但是动态处理能力不足,因此,在企业中常用动静分离技术

# 动静分离

动静分离技术其实是采用代理的方式,在server{}段中加入带正则匹配的location来指定匹配项针对服务的动静分离。

静态页面交给Nginx处理,动态页面交给后台服务处理

# 静态资源存储在目录

在Nginx的配置中,是通过location配置段配合正则匹配,来实现静态与动态页面的不同处理方式。

location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css){  
    root   /soft/nginx/static_resources;  
    expires 7d;  
}
1
2
3
4

说明:

  • ~代表匹配时区分大小写;
  • .*代表任意字符都可以出现零次或多次,即资源名不限制;
  • \.代表匹配后缀分隔符.
  • (html|...|css)代表匹配括号里所有静态资源类型;
  • /soft/nginx/static_resources为自建目录;

# 静态资源存储在远程服务器

也可以将静态资源上传到文件服务器中,然后location中配置一个新的指向

案例:实现网站的动静分离,实现如下要求:

  1. 前端Nginx收到静态请求,直接从NFS中返回给客户端。

  2. 前端Nginx收到动态请求转交给通过FastCGI交给服务器处理。

    • 如果得到静态结果直接从NFS取出结果交给Nginx然后返回给客户端。

    • 如果需要数据处理服务器连接数据库后将结果返回给Nginx。

  3. 前端Nginx收到图片请求以.jpg、.png、.gif等请求交给后端Images服务器处理。

location ~* \.(jpg|gif)$ {             # location匹配将图片交给Image处理
  proxy_pass http://10.10.0.23:80;      # Image服务器要开启web服务
}
1
2
3

至此配置就已经完成。达到了图片从图片服务器返回,静态资源由Nginx直接返回,动态资源交给后端进行处理。

说明:

  1. 前端Nginx要做好location匹配,将*.php与*.jpg等各类资源分别进行反向代理。

  2. 后端Image服务器要开启WEB服务。根目录要指向图片根目录,且根目录下的图片要与原本图片文件目录结构一致。

# Nginx镜像服务器

Nginx的proxy_store作用是直接把静态文件在本地硬盘创建并读取。类似于七牛这样的镜像CDN功能

首次访问会自动获取源站的静态图片等文件,之后的访问就是直接从CDN服务器读取(即当前Nginx配置的缓存目录),加快了速度。

# 参数说明

#启用缓存到本地的功能
proxy_store on;
#表示用户读写权限,如果在error中报路径不允许访问的话就用"chomod -R a+rw"将下面配置的路径改为相应的权限.
proxy_store_access user:rw group:rw all:rw;
#此处为文件的缓存路径,这个路径是和url中的文件路径一致的
proxy_temp_path 缓存目录;
#在上面的配置之后,虽然文件被缓存到了本地磁盘上,但每次请求仍会向远端拉取文件,为了避免去远端拉取文件,还必须增加:
if ( !-e $request_filename) {
  proxy_pass http://192.168.10.10;
}
1
2
3
4
5
6
7
8
9
10

说明:

  1. "!-e $request_filename":正则表达式,匹配缓存目录中的文件与源文件是否存在。
  2. proxy_pass:源服务器的地址,默认端口80。如监听其他端口此处要指出,例如4000端口,http://192.168.10.10:4000。

# 整体配置

location / {                //这里的location是要换成自己经过精确匹配的location,比如要缓存图片要写成 "location ~*\.(gif|jpg|jepg|png|bmp)${"
  expires 3d;               //所有链接,浏览器缓存过期时间为3天
  proxy_set_header Accept-Encoding '';
  root /home/mpeg/nginx;    //此目录为服务器的根目录,下面的if语句就是判断此目录下是否有响应的文件
  proxy_store on;           //表示开启缓存
  proxy_store_access user:rw group:rw all:rw;	//表示用户读写权限
  proxy_temp_path /home/mpeg/nginx;    //此处为文件的缓存路径,这个路径是和url中的文件路径一致的
  if ( !-e $request_filename) {
      proxy_pass http://192.168.0.1;    //此处为要被代理的服务器的地址
  }
}
1
2
3
4
5
6
7
8
9
10
11

# 六、Nginx资源压缩

建立在动静分离的基础之上,如果一个静态资源的Size越小,那么自然传输速度会更快,同时也会更节省带宽。

因此我们在部署项目时,也可以通过Nginx对于静态资源实现压缩传输

  1. 一方面可以节省带宽资源
  2. 第二方面也可以加快响应速度提升系统整体吞吐

在Nginx也提供了三个支持资源压缩的模块:

  • ngx_http_gzip_module
  • ngx_http_gzip_static_module
  • ngx_http_gunzip_module

# ngx_http_gzip_module

其中ngx_http_gzip_module属于内置模块,代表着可以直接使用该模块下的一些压缩指令

后续的资源压缩操作都基于该模块。先来看看压缩配置的一些参数/指令:

# Nginx中配置资源压缩

http{
    # 开启压缩机制
    gzip on;
    # 指定会被压缩的文件类型(也可自己配置其他类型)
    gzip_types text/plain application/javascript text/css application/xml text/javascript image/jpeg image/gif image/png;
    # 设置压缩级别,越高资源消耗越大,但压缩效果越好
    gzip_comp_level 5;
    # 在头部中添加Vary: Accept-Encoding(建议开启)
    gzip_vary on;
    # 处理压缩请求的缓冲区数量和大小
    gzip_buffers 16 8k;
    # 对于不支持压缩功能的客户端请求不开启压缩机制
    gzip_disable "MSIE [1-6]\."; # 低版本的IE浏览器不支持压缩
    # 设置压缩响应所支持的HTTP最低版本
    gzip_http_version 1.1;
    # 设置触发压缩的最小阈值
    gzip_min_length 2k;
    # 关闭对后端服务器的响应结果进行压缩
    gzip_proxied off;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

在上述的压缩配置中,最后一个gzip_proxied选项,可以根据系统的实际情况决定。总共存在多种选项:

  1. off:关闭Nginx对后台服务器的响应结果进行压缩。
  2. expired:如果响应头中包含Expires信息,则开启压缩。
  3. no-cache:如果响应头中包含Cache-Control:no-cache信息,则开启压缩。
  4. no-store:如果响应头中包含Cache-Control:no-store信息,则开启压缩。
  5. private:如果响应头中包含Cache-Control:private信息,则开启压缩。
  6. no_last_modified:如果响应头中不包含Last-Modified信息,则开启压缩。
  7. no_etag:如果响应头中不包含ETag信息,则开启压缩。
  8. auth:如果响应头中包含Authorization信息,则开启压缩。
  9. any:无条件对后端的响应结果开启压缩机制。

# 测试案例

在首页的index页面中引入一个jquery-3.6.0.js文件:

<script type="text/javascript" src="jquery-3.6.0.js"></script>
1

分别来对比下压缩前后的区别:

从图中可以很明显看出,未开启压缩机制前访问时,js文件的原始大小为230KB

当配置好压缩后再重启Nginx,会发现文件大小从230KB降至69KB,效果立竿见影!

注意点:

  1. 对于图片、视频类型的数据,Nginx 会默认开启压缩机制,因此一般无需再次开启压缩。
  2. 对于.js文件而言,需要指定压缩类型为application/javascript,而并非text/javascript、application/x-javascript

# 七、Nginx缓冲区buffer

接入Nginx的项目一般请求流程为:“客户端→Nginx→服务端”。

在这个过程中存在两个连接:“客户端→NginxNginx→服务端”,那么这两个连接的速度不一致,就会影响用户的体验(比如浏览器的加载速度,跟不上服务端的响应速度)。

其实也就类似电脑的内存跟不上CPU速度,所以对于用户造成的体验感极差,因此在CPU设计时都会加入三级高速缓冲区,用于缓解CPU和内存速率不一致的矛盾。

在Nginx也同样存在缓冲区(buffer)的机制,主要目的就在于:用来解决两个连接之间速度不匹配造成的问题

有了缓冲后,Nginx代理可暂存后端的响应,然后按需供给数据给客户端。

作用:使用缓冲也可以减少即时传输带来的带宽消耗

# Nginx缓冲区配置项

  • proxy_buffering:是否启用缓冲机制,默认为on关闭状态。

  • client_body_buffer_size:设置缓冲客户端请求数据的内存大小。

  • proxy_buffers:为每个请求/连接设置缓冲区的数量和大小,默认4 4k/8k

  • proxy_buffer_size:设置用于存储响应头的缓冲区大小。

  • proxy_busy_buffers_size:该参数用来设置busy状态的buffer具体有多大,默认为proxy_buffer_size*2

    在后端数据没有完全接收完成时,Nginx可以将busy状态的缓冲返回给客户端。

  • proxy_temp_path:当内存缓冲区存满时,可以将数据临时存放到磁盘,该参数是设置存储缓冲数据的目录。

    • 语法:proxy_temp_path path; path是临时目录的路径。
  • proxy_temp_file_write_size:设置每次写数据到临时文件的大小限制。

  • proxy_max_temp_file_size:设置临时的缓冲目录中允许存储的最大容量。

  • 非缓冲参数项:

    • proxy_connect_timeout:设置与后端服务器建立连接时的超时时间。
    • proxy_read_timeout:设置从后端服务器读取响应数据的超时时间。
    • proxy_send_timeout:设置向后端服务器传输请求数据的超时时间。

# Nginx中配置缓冲区

具体的nginx.conf配置如下:

http{  
    proxy_connect_timeout 10;  
    proxy_read_timeout 120;  
    proxy_send_timeout 10;  
    proxy_buffering on;  
    client_body_buffer_size 512k;  
    proxy_buffers 4 64k;  
    proxy_buffer_size 16k;  
    proxy_busy_buffers_size 128k;  
    proxy_temp_file_write_size 128k;  
    proxy_temp_path /soft/nginx/temp_buffer;  
}
1
2
3
4
5
6
7
8
9
10
11
12

上述的缓冲区参数,是基于每个请求分配的空间,而并不是所有请求的共享空间。

当然,具体的参数值还需要根据业务去决定,要综合考虑机器的内存以及每个请求的平均数据大小。

# 八、Nginx缓存机制cache

对于性能优化而言,缓存(cache)是一种能够大幅度提升性能的方案,因此几乎可以在各处都能看见缓存,如客户端缓存、代理缓存、服务器缓存等等。

Nginx的缓存则属于代理缓存的一种。

对于整个系统而言,加入缓存带来的优势额外明显:

  1. 减少了再次向后端或文件服务器请求资源的带宽消耗
  2. 降低了下游服务器的访问压力提升系统整体吞吐
  3. 缩短了响应时间,提升了加载速度,打开页面的速度更快

# Nginx缓存配置项

1)proxy_cache_path

代理缓存的路径。语法:

proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
1

是的,你没有看错,就是这么长....。解释一下每个参数项的含义:

  • path:缓存的路径地址。
  • levels:缓存存储的层次结构,最多允许三层目录。
  • use_temp_path:是否使用临时目录。
  • keys_zone:指定一个共享内存空间来存储热点Key(1M可存储8000个Key)。
  • inactive:设置缓存多长时间未被访问后删除(默认是十分钟)。
  • max_size:允许缓存的最大存储空间,超出后会基于LRU算法移除缓存,Nginx会创建一个Cache manager的进程移除数据,也可以通过purge方式。
  • manager_files:manager进程每次移除缓存文件数量的上限。
  • manager_sleep:manager进程每次移除缓存文件的时间上限。
  • manager_threshold:manager进程每次移除缓存后的间隔时间。
  • loader_files:重启Nginx载入缓存时,每次加载的个数,默认100。
  • loader_sleep:每次载入时,允许的最大时间上限,默认200ms。
  • loader_threshold:一次载入后,停顿的时间间隔,默认50ms。
  • purger:是否开启purge方式移除数据。
  • purger_files:每次移除缓存文件时的数量。
  • purger_sleep:每次移除时,允许消耗的最大时间。
  • purger_threshold:每次移除完成后,停顿的间隔时间。

2)proxy_cache

开启或关闭代理缓存,开启时需要指定一个共享内存区域。语法:

proxy_cache zone | off;
1

zone为内存区域的名称,即上面中keys_zone设置的名称。

3)proxy_cache_key

定义如何生成缓存的键。语法:

proxy_cache_key string;
1

string为生成Key的规则,如$scheme$proxy_host$request_uri

4)proxy_cache_valid

缓存生效的状态码与过期时间。语法:

proxy_cache_valid [code ...] time;
1

code为状态码,time为有效时间,可以根据状态码设置不同的缓存时间。

例如:proxy_cache_valid 200 302 30m;

5)proxy_cache_min_uses

设置资源被请求多少次后被缓存。语法:

proxy_cache_min_uses number;
1

number为次数,默认为1。

6)proxy_cache_use_stale

当后端出现异常时,是否允许Nginx返回缓存作为响应。语法:

proxy_cache_use_stale error;
1

error为错误类型,可配置timeout|invalid_header|updating|http_500...

7)proxy_cache_lock

对于相同的请求,是否开启锁机制,只允许一个请求发往后端。语法:

proxy_cache_lock on | off;
1

8)proxy_cache_lock_timeout

配置锁超时机制,超出规定时间后会释放请求。

proxy_cache_lock_timeout time;
1

9)proxy_cache_methods

设置对于那些HTTP方法开启缓存。语法:

proxy_cache_methods method;
1

method为请求方法类型,如GET、HEAD等。

10)proxy_no_cache

定义不存储缓存的条件,符合时不会保存。语法:

proxy_no_cache string...;
1

string为条件,例如$cookie_nocache $arg_nocache $arg_comment;

11)proxy_cache_bypass

定义不读取缓存的条件,符合时不会从缓存中读取。语法:

proxy_cache_bypass string...;
1

和上面proxy_no_cache的配置方法类似。

12)add_header

往响应头中添加字段信息。语法:

add_header fieldName fieldValue;
1

13)$upstream_cache_status

记录了缓存是否命中的信息。之前的都是参数项,这个是一个Nginx内置变量。存在多种情况:

  • MISS:请求未命中缓存。
  • HIT:请求命中缓存。
  • EXPIRED:请求命中缓存但缓存已过期。
  • STALE:请求命中了陈旧缓存。
  • REVALIDDATED:Nginx验证陈旧缓存依然有效。
  • UPDATING:命中的缓存内容陈旧,但正在更新缓存。
  • BYPASS:响应结果是从原始服务器获取的。

# Nginx中配置缓存

http{  
    # 设置缓存的目录,并且内存中缓存区名为hot_cache,大小为128m,  
    # 三天未被访问过的缓存自动清楚,磁盘中缓存的最大容量为2GB。
    proxy_cache_path /soft/nginx/cache levels=1:2 keys_zone=hot_cache:128m inactive=3d max_size=2g;  
      
    server{  
        location / {  
            # 使用名为nginx_cache的缓存空间  
            proxy_cache hot_cache;  
            # 对于200、206、304、301、302状态码的数据缓存1天  
            proxy_cache_valid 200 206 304 301 302 1d;  
            # 对于其他状态的数据缓存30分钟  
            proxy_cache_valid any 30m;  
            # 定义生成缓存键的规则(请求的url+参数作为key)  
            proxy_cache_key $host$uri$is_args$args;  
            # 资源至少被重复访问三次后再加入缓存  
            proxy_cache_min_uses 3;  
            # 出现重复请求时,只让一个去后端读数据,其他的从缓存中读取  
            proxy_cache_lock on;  
            # 上面的锁超时时间为3s,超过3s未获取数据,其他请求直接去后端  
            proxy_cache_lock_timeout 3s;  
            # 对于请求参数或cookie中声明了不缓存的数据,不再加入缓存  
            proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;  
            # 在响应头中添加一个缓存是否命中的状态(便于调试)  
            add_header Cache-status $upstream_cache_status;  
        }  
    }  
}
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

接着来看一下效果,如下:

第一次访问时,因为还没有请求过资源,所以缓存中没有数据,因此没有命中缓存。第二、三次,依旧没有命中缓存,直至第四次时才显示命中,这是为什么呢?

因为在前面的缓存配置中,我们配置了加入缓存的最低条件为:资源至少要被请求三次以上才会加入缓存 ,这样可以避免很多无效缓存占用空间。

# 缓存清理

当缓存过多时,如果不及时清理会导致磁盘空间被“吃光”,因此我们需要一套完善的缓存清理机制去删除缓存。

在之前的proxy_cache_path参数中有purger相关的选项,开启后可以帮我们自动清理缓存。但遗憾的是:purger系列参数只有商业版的NginxPlus才能使用,因此需要付费才可使用。

不过天无绝人之路,我们可以通过强大的第三方模块ngx_cache_purge来替代。先来安装一下该插件:

  1. 首先去到Nginx的安装目录下,创建一个cache_purge目录:

    [root@localhost]# mkdir cache_purge && cd cache_purge
    
    1
  2. 通过wget指令从github上拉取安装包的压缩文件并解压:

    [root@localhost]# wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz  
    [root@localhost]# tar -xvzf 2.3.tar.gz  
    
    1
    2
  3. 再次去到之前Nginx的解压目录下:

    [root@localhost]# cd /soft/nginx/nginx1.21.6  
    
    1
  4. 重新构建一次Nginx,通过--add-module的指令添加刚刚的第三方模块:

    [root@localhost]# ./configure --prefix=/soft/nginx/ --add-module=/soft/nginx/cache_purge/ngx_cache_purge-2.3/  
    
    1
  5. 重新根据刚刚构建的Nginx,再次编译一下,「但切记不要make install」

    [root@localhost]# make  
    
    1
  6. 删除之前Nginx的启动文件,不放心的也可以移动到其他位置:

    [root@localhost]# rm -rf /soft/nginx/sbin/nginx  
    
    1
  7. 从生成的objs目录中,重新复制一个Nginx的启动文件到原来的位置:

    [root@localhost]# cp objs/nginx /soft/nginx/sbin/nginx  
    
    1

    至此,第三方缓存清除模块ngx_cache_purge就安装完成了,接下来稍微修改一下nginx.conf配置,再添加一条location规则:

    location ~ /purge(/.*) {  
      # 配置可以执行清除操作的IP(线上可以配置成内网机器)  
      # allow 127.0.0.1; # 代表本机  
      allow all; # 代表允许任意IP清除缓存  
      proxy_cache_purge $host$1$is_args$args;  
    }  
    
    1
    2
    3
    4
    5
    6

    然后再重启Nginx,接下来即可通过http://xxx/purge/xx的方式清除缓存。

# 九、Nginx实现IP黑白名单

有时候往往有些需求,可能某些接口只能开放给对应的合作商,或者购买/接入API的合作伙伴,那么此时就需要实现类似于IP白名单的功能。

而有时候有些恶意攻击者或爬虫程序,被识别后需要禁止其再次访问网站,因此也需要实现IP黑名单。

那么这些功能无需交由后端实现,可直接在Nginx中处理。

Nginx黑白名单机制,主要是通过allow、deny配置项来实现:

allow xxx.xxx.xxx.xxx; # 允许指定的IP访问,可以用于实现白名单。  
deny xxx.xxx.xxx.xxx; # 禁止指定的IP访问,可以用于实现黑名单。  
1
2

# 文件形式配置

要同时屏蔽/开放多个IP访问时,如果所有IP全部写在nginx.conf文件中定然是不显示的,这种方式比较冗余。

那么可以新建两个文件BlocksIP.conf、WhiteIP.conf

# --------黑名单:BlocksIP.conf---------  
deny 192.177.12.222; # 屏蔽192.177.12.222访问  
deny 192.177.44.201; # 屏蔽192.177.44.201访问  
deny 127.0.0.0/8; # 屏蔽127.0.0.1到127.255.255.254网段中的所有IP访问  
  
# --------白名单:WhiteIP.conf---------  
allow 192.177.12.222; # 允许192.177.12.222访问  
allow 192.177.44.201; # 允许192.177.44.201访问  
allow 127.45.0.0/16; # 允许127.45.0.1到127.45.255.254网段中的所有IP访问  
deny all; # 除开上述IP外,其他IP全部禁止访问  
1
2
3
4
5
6
7
8
9
10

分别将要禁止/开放的IP添加到对应的文件后,然后再将这两个文件在nginx.conf中导入:

http{  
    # 屏蔽该文件中的所有IP  
    include /soft/nginx/IP/BlocksIP.conf;   
 server{  
    location xxx {  
        # 某一系列接口只开放给白名单中的IP  
        include /soft/nginx/IP/blockip.conf;   
    }  
 }  
}
1
2
3
4
5
6
7
8
9
10

对于文件具体在哪儿导入(include),这个也并非随意的。

  • 如果要整站屏蔽/开放就在http中导入;
  • 如果只需要一个域名下屏蔽/开放就在sever中导入;
  • 如果只需要针对于某一系列接口屏蔽/开放IP,那么就在location中导入。

# 第三方库实现

当然,上述只是最简单的IP黑/白名单实现方式,同时也可以通过ngx_http_geo_module、ngx_http_geo_module第三方库去实现(这种方式可以按地区、国家进行屏蔽,并且提供了IP库)。

# 十、Nginx跨域配置

跨域问题在之前的单体架构开发中,其实是比较少见的问题,除非是需要接入第三方SDK时,才需要处理此问题。但随着现在前后端分离、分布式架构的流行,跨域问题也成为了每个Java开发必须要懂得解决的一个问题。

# 跨域问题产生的原因

产生跨域问题的主要原因就在于同源策略,为了保证用户信息安全,防止恶意网站窃取数据,同源策略是必须的,否则cookie可以共享。由于http无状态协议通常会借助cookie来实现有状态的信息记录,例如用户的身份/密码等,因此一旦cookie被共享,那么会导致用户的身份信息被盗取。

同源策略主要是指三点相同,协议+域名+端口 相同的两个请求,则可以被看做是同源的,但如果其中任意一点存在不同,则代表是两个不同源的请求,同源策略会限制了不同源之间的资源交互。

# Nginx解决跨域问题

弄明白了跨域问题的产生原因,接下来看看Nginx中又该如何解决跨域呢?其实比较简单,在nginx.conf中稍微添加一点配置即可:

location / {  
    # 允许跨域的请求,可以自定义变量$http_origin,*表示所有  
    add_header 'Access-Control-Allow-Origin' *;  
    # 允许携带cookie请求  
    add_header 'Access-Control-Allow-Credentials' 'true';  
    # 允许跨域请求的方法:GET,POST,OPTIONS,PUT  
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT';  
    # 允许请求时携带的头部信息,*表示所有  
    add_header 'Access-Control-Allow-Headers' *;  
    # 允许发送按段获取资源的请求  
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';  
    # 一定要有!!!否则Post请求无法进行跨域!
    # 在发送Post跨域请求前,会以Options方式发送预检请求,服务器接受时才会正式请求  
    if ($request_method = 'OPTIONS') {  
        add_header 'Access-Control-Max-Age' 1728000;  
        add_header 'Content-Type' 'text/plain; charset=utf-8';  
        add_header 'Content-Length' 0;  
        # 对于Options方式的请求返回204,表示接受跨域请求  
        return 204;  
    }  
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

nginx.conf文件加上如上配置后,跨域请求即可生效了。

但如果后端是采用分布式架构开发的,有时候RPC调用也需要解决跨域问题,不然也同样会出现无法跨域请求的异常。因此需要在你的后端项目中,通过继承HandlerInterceptorAdapter类、实现WebMvcConfigurer接口、添加@CrossOrgin注解的方式实现接口之间的跨域配置。

# 十一、Nginx防盗链设计

# 盗链

首先了解一下何谓盗链:盗链即是指外部网站引入当前网站的资源对外展示

来举个简单的例子理解:好比壁纸网站X站、Y站。

  • X站是一点点去购买版权、签约作者的方式,从而积累了海量的壁纸素材。
  • 但Y站由于资金等各方面的原因,就直接通过标签imgsrc="X站/xxx.jpg"这种方式照搬了X站的所有壁纸资源,继而提供给用户下载。

那么如果我们自己是这个X站的Boss,心中必然不爽,那么此时又该如何屏蔽这类问题呢?那么接下来要叙说的防盗链 登场了!

# Nginx的防盗链机制

Nginx的防盗链机制实现,跟一个头部字段:Referer有关,该字段主要描述了当前请求是从哪儿发出的。

那么在Nginx中就可获取该值,然后判断是否为本站的资源引用请求,如果不是则不允许访问。

Nginx中存在一个配置项为valid_referers,正好可以满足前面的需求,语法如下:

valid_referers none | blocked | server_names | string ...;
1
  • none:表示接受没有Referer字段的HTTP请求访问。
  • blocked:表示允许http://https//以外的请求访问。
  • server_names:资源的白名单,这里可以指定允许访问的域名。
  • string:可自定义字符串,支配通配符、正则表达式写法。

简单了解语法后,接下来的实现如下:

location ~* \.(gif|jpg|jpeg|png|js|css){
    # 设置允许访问的一个或多个IP/域名地址
    valid_referers *.xxxx.cn xxxx.cn;
    if ($invalid_referer) {
        return   403;
    }
    root /usr/share/nginx/html/;
}
1
2
3
4
5
6
7
8

根据上述中的内容配置后,就已经通过Nginx实现了最基本的防盗链机制,最后只需要额外重启一下就好啦!

# 第三方库实现

当然,对于防盗链机制实现这块,也有专门的第三方模块ngx_http_accesskey_module实现了更为完善的设计,感兴趣的小伙伴可以自行去看看。

PS:防盗链机制也无法解决爬虫伪造referers信息的这种方式抓取数据。

# 十二、Nginx大文件传输配置

在某些业务场景中需要传输一些大文件,但大文件传输时往往都会会出现一些Bug。比如文件超出限制、文件传输过程中请求超时等,那么此时就可以在Nginx稍微做一些配置。

# Nginx传输配置项

先来了解一些关于大文件传输时可能会用的配置项:

在传输大文件时,client_max_body_sizeclient_header_timeoutproxy_read_timeoutproxy_send_timeout这四个参数值都可以根据自己项目的实际情况来配置。

上述配置仅是作为代理层需要配置的,因为最终客户端传输文件还是直接与后端进行交互,这里只是把作为网关层的Nginx配置调高一点,调到能够“容纳大文件”传输的程度。

# Nginx作为文件服务器

当然,Nginx中也可以作为文件服务器使用,但需要用到一个专门的第三方模块nginx-upload-module

  • 如果项目中文件上传的作用处不多,那么建议可以通过Nginx搭建,毕竟可以节省一台文件服务器资源。
  • 如果文件上传/下载较为频繁,那么还是建议额外搭建文件服务器,并将上传/下载功能交由后端处理。

# 十三、Nginx配置SLL证书

随着越来越多的网站接入HTTPS,因此Nginx中仅配置HTTP还不够,往往还需要监听443端口的请求,HTTPS为了确保通信安全,所以服务端需配置对应的数字证书

当项目使用Nginx作为网关时,那么证书在Nginx中也需要配置。

SSL证书配置过程:

  1. 先去CA机构或从云控制台中申请对应的SSL证书,审核通过后下载Nginx版本的证书。

  2. 下载数字证书后,完整的文件总共有三个:.crt、.key、.pem

    • .crt:数字证书文件,.crt.pem的拓展文件,因此有些人下载后可能没有。

    • .key:服务器的私钥文件,及非对称加密的私钥,用于解密公钥传输的数据。

    • .pemBase64-encoded编码格式的源证书文本文件,可自行根需求修改拓展名。

  3. 在Nginx目录下新建certificate目录,并将下载好的证书/私钥等文件上传至该目录。

  4. 最后修改一下nginx.conf文件即可,如下:

# ----------HTTPS配置-----------  
server {  
    # 监听HTTPS默认的443端口  
    listen 443;  
    # 配置自己项目的域名  
    server_name www.xxx.com;  
    # 打开SSL加密传输  
    ssl on;  
    # 输入域名后,首页文件所在的目录  
    root html;  
    # 配置首页的文件名  
    index index.html index.htm index.jsp index.ftl;  
    # 配置自己下载的数字证书  
    ssl_certificate  certificate/xxx.pem;  
    # 配置自己下载的服务器私钥  
    ssl_certificate_key certificate/xxx.key;  
    # 停止通信时,加密会话的有效期,在该时间段内不需要重新交换密钥  
    ssl_session_timeout 5m;  
    # TLS握手时,服务器采用的密码套件  
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;  
    # 服务器支持的TLS版本  
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;  
    # 开启由服务器决定采用的密码套件  
    ssl_prefer_server_ciphers on;  
  
    location / {  
        ....  
    }  
}  
  
# ---------HTTP请求转HTTPS-------------  
server {  
    # 监听HTTP默认的80端口  
    listen 80;  
    # 如果80端口出现访问该域名的请求  
    server_name www.xxx.com;  
    # 将请求改写为HTTPS(这里写你配置了HTTPS的域名)  
    rewrite ^(.*)$ https://www.xxx.com;  
}
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

根据如上配置了Nginx后,你的网站即可通过https://的方式访问,并且当客户端使用http://的方式访问时,会自动将其改写为HTTPS请求。

# 十四、Nginx 配置文件解读

Nginx 配置文件:nginx.conf

# 配置文件分类

nginx配置文件主要分为六个核心区域

  • main(全局设置) 作用域是全局
  • events(nginx工作模式)
  • upstream(负载均衡服务器设置)
  • http(http设置)
    • sever(主机设置)
      • location(URL匹配)

# nginx.conf内容

#设置用户的权限  root nobody 指定 用户名虚拟机内用户   或者 Ip访问 
#user  nobody;
#设置工作进程数 一般为 Cpu 核心*2  4*2 
worker_processes  8;  
# 日志输出参数 
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
# 进程ID 
#pid        logs/nginx.pid;

events {
#指定运行模型 
    use epoll;
# 工作连接数  默认512 根据自己的情况调整 
    worker_connections  1024;
}

#http模块 
http {
#  能够支持的类型 在 这个文件下写着  mime.types
    include       mime.types;
# 默认的类型  在 application/octet-stream;
    default_type  application/octet-stream;
# 日志的格式 
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
#访问日志记录 
    #access_log  logs/access.log  main;
#启动 发送文件 
    sendfile        on;
# 开启TCP 推送 
    #tcp_nopush     on;
# 连接超时时间 
    #keepalive_timeout  0;
    keepalive_timeout  65;
# 开启压缩文件 
    #gzip  on;
# 服务 
# 服务分组  反向代理的核心关键 
 upstream tuling {
# ip 方式 最大失败3个连接  间隔 30S  权重为 5
        server 127.0.0.1:8080       max_fails=3 fail_timeout=30s weight=5;
#根据ip 利用Hash算法决定访问哪台机器 
   ip_hash;
    }
    server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;
#访问日志记录 以及位置  
        #access_log  logs/host.access.log  main;
# 匹配位置 支持正则表达式 
        location / {
# 寻找位置 默认在Nginx 目录下的  类型 
            root   html;
            index  index.html index.htm;
			 proxy_pass   http://127.0.0.1;
        }
#错误信息 页面 
        #error_page  404              /404.html;
#将服务器错误页重定向到静态页/50x.html
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
#实例 入 将访问尾缀为 \.php 跳转到 127.0.0.1
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}
#将PHP脚本传递给正在侦听127.0.0.1:9000的FastCGI服务器
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}
#拒绝访问.htaccess文件,如果Apache的文档根
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

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


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

# 内容详解描述


################### main区域 #################################
#user :来指定Nginx Worker进程运行用户以及用户组,默认由nobody账号运行。也可以创建nginx用户指定用户。
#    创建www用户,在nginx配置文件中把user noboby noboby;-->user www www;即可
#    /usr/sbin/groupadd www 
#    /usr/sbin/useradd -g www www
#worker_processes:来指定了Nginx要开启的子进程数。每个Nginx进程平均耗费10M~12M内存。根据经验,一般指定1个进程就足够了,如果是多核CPU,
#    建议指定和CPU的数量一样的进程数即可。我这里写2,那么就会开启2个子进程,总共3个进程。
#error_log:用来定义全局错误日志文件。日志输出级别有debug、info、notice、warn、error、crit可供选择,其中,debug输出日志最为最详细,而crit输出日志最少。
#pid:用来指定进程id的存储文件位置。
#worker_rlimit_nofile:用于指定一个nginx进程可以打开的最多文件描述符数目,这里是65535,需要使用命令“ulimit -n 65535”来设置。

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;


#####################event 区域###############################
#use:用来指定Nginx的工作模式。Nginx支持的工作模式有select、poll、kqueue、epoll、rtsig和/dev/poll。
#    其中select和poll都是标准的工作模式,kqueue和epoll是高效的工作模式,不同的是epoll用在Linux平台上,
#    而kqueue用在BSD系统中,对于Linux系统,epoll工作模式是首选。
#worker_connections:用于定义Nginx每个进程的最大连接数,即接收前端的最大请求数,默认是1024。
#    最大客户端连接数由worker_processes和worker_connections决定,即Max_clients=worker_processes*worker_connections,
#   在作为反向代理时,Max_clients变为:Max_clients = worker_processes * worker_connections/4。 
#    进程的最大连接数受Linux系统进程的最大打开文件数限制,在执行操作系统命令“ulimit -n 65536”后worker_connections的设置才能生效。

events {
    use epoll;
    worker_connections  1024;
}
######################### http设置#####################################
#    http模块负责HTTP服务器相关属性的配置,有server和upstream两个子模块
http {
#include :来用设定文件的mime类型,类型在配置文件目录下的mime.type文件定义,来告诉nginx来识别文件类型。
#default_type:设定了默认的类型为二进制流,也就是当文件类型未定义时使用这种方式,例如在没有配置asp的locate环境时,Nginx是不予解析的,此时,用浏览器访问asp文件就会出现下载了。
#log_format:用于设置日志的格式,和记录哪些参数,这里设置为main,刚好用于access_log来纪录这种类型。
    include       mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;
    sendfile        on;
    tcp_nopush     on;
    keepalive_timeout  0;
    keepalive_timeout  65;
    gzip  on;

######################### server设置#####################################
#server用来定一个虚拟主机,标志定义虚拟主机开始。
#listen:用于指定虚拟主机的服务端口。
#server_name:用来指定IP地址或者域名,多个域名之间用空格分开。
#root :表示在这整个server虚拟主机内,全部的root web根目录。注意要和locate {}下面定义的区分开来。
#index :全局定义访问的默认首页地址。注意要和locate {}下面定义的区分开来。
#charset:用于设置网页的默认编码格式。
#access_log:用来指定此虚拟主机的访问日志存放路径,最后的main用于指定访问日志的输出格式。
    server {
        listen       80;
        server_name  localhost;
        root   /Users/hk/www;
        index  index.php index.html index.htm; 
        charset utf-8;
        access_log  logs/host.access.log  main;
        aerror_log  logs/host.error.log   main;

######################### location设置#####################################
# location模块 负载均衡,反向代理,虚拟域名等配置。是来定位的,定位URL,解析URL,它也提供了强大的正则匹配功能,也支持条件判断匹配,
#    可以通过location指令实现Nginx对动,静态网页进行过滤处理。
#/表示匹配访问根目录。
#root指令用于指定访问根目录时,虚拟主机的web目录,这个目录可以是相对路径(相对路径是相对于nginx的安装目录)。也可以是绝对路径。
#proxy_pass:代理转发,如果在proxy_pass后面的url加/,表示绝对根路径;如果没有/,表示相对路径,把匹配的路径部分也给代理走。
#proxy_set_header:允许重新定义或者添加发往后端服务器的请求头。
#include:加载配置文件,后面介绍nginx多个配置文件时候会提到。
#root:定位localtion匹配的url资源路径。
#index:定义页面显示html,一般和alias配合使用。
        location / {       
            root   html;    
            index  index.html index.htm;
        }

        error_page  404              /404.html;       
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        
        
        #反向代理配置
        location /jyb {
            proxy_pass http://qurt/;
            proxy_read_timeout 1800s;
            proxy_set_header   Host $host:$server_port;
            proxy_set_header   X-real-ip  $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;  
            proxy_set_header   X-Forwarded-Proto  $scheme; 
         }
     
        
         #采用uwsgi方式
         location /python/ {
             include uwsgi_params;
             uwsgi_pass 127.0.0.1:33333;
         }
        
        # FastCGI方式
        location ~ \.php$ {
            root           html;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            include        fastcgi_params;
        }
        
        #访问nginx本机目录的文件
        location / {
            root   /home/hk/;
            index  index.html index.htm;
        }
        
        location  /static/ {
             alias /var/static/;
        }

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        location ~ /\.ht {
            deny  all;
        }
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    server {
        listen       8000;
        listen       somename:8080;
        server_name  somename  alias  another.alias;

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


    # HTTPS server    
    server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate      cert.pem;
        ssl_certificate_key  cert.key;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }
    
##############upstram 模块################
# upstream 模块  负载均衡模块,通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡。
#Nginx的负载均衡模块目前支持4种调度算法:
#    weight 轮询(默认)。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。
#        weight指定轮询权值,weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。
#    ip_hash。每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。
#    fair。比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,
#        也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。
#    url_hash。按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身是不支持url_hash的,
#        如果需要使用这种调度算法,必须安装Nginx 的hash软件包。

#在HTTP Upstream模块中,可以通过server指令指定后端服务器的IP地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:
#    down,表示当前的server暂时不参与负载均衡。
#    backup,预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因此这台机器的压力最轻。
#    max_fails,允许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。
#    fail_timeout,在经历了max_fails次失败后,暂停服务的时间。max_fails可以和fail_timeout一起使用。

#注意 当负载调度算法为ip_hash时,后端服务器在负载均衡调度中的状态不能是weight和backup。
#备注: nginx的worker_rlimit_nofile达到上限时,再有客户端链接报502错误. 用了log_format指令设置了日志格式之后,需要用access_log指令指定日志文件的存放路径。
    
    upstream server_group {
        ip_hash;
        server 192.168.123.1:80;
        server 192.168.123.2:80 down;
        server 192.168.123.3:8080  max_fails=3  fail_timeout=20s;
        server 192.168.123.4:8080;
    }
    
    server {
        listen       80;
        server_name  localhost;

        location / {
            proxy_pass http://server_group/;
        }
    }

}


######################nginx 中location中root和alias的区别 ####################
    nginx指定文件路径有两种方式root和alias,这两者的用法区别,使用方法总结了。
    root与alias主要区别在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。
    [root]
        语法:root path
        默认值:root html
        配置段:http、server、location、if

    [alias]
        语法:alias path
        配置段:location

    root实例:

        location ^~ /t/ {
            root /www/root/html/;
        }
        如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/t/a.html的文件。

    alias实例:
        location ^~ /t/ {
            alias /www/root/html/new_t/;
        }
        如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/new_t/a.html的文件。注意这里是new_t,
        因为alias会把location后面配置的路径丢弃掉,把当前匹配到的目录指向到指定的目录。
    注意:
        1. 使用alias时,目录名后面一定要加"/"。
        2. alias在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用。
        3. alias只能位于location块中。(root可以不放在location中)
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

# 十五、Nginx高可用部署

线上如果采用单个节点的方式部署Nginx,难免会出现天灾人祸,比如系统异常、程序宕机、服务器断电、机房爆炸....。但实际生产环境中确实存在隐患问题,由于Nginx作为整个系统的网关层接入外部流量,所以一旦Nginx宕机,最终就会导致整个系统不可用,这无疑对于用户的体验感是极差的,因此也得保障Nginx高可用的特性。

# 部署方案

采用keepalived+Nginx,实现Nginx双机热备部署(双主模式)。

我们常用nginx做负载均衡,作为架构的最前端或中间层,随着日益增长的访问量,需要给负载均衡做高可用架构。

可利用通过keepalivedVIP机制(Virtual IP、虚拟IP),解决单点故障问题,实现Nginx的高可用。一旦 nginx宕机能快速切换到备份服务器。

# Linux部署步骤

  • 启动 两台的nginx;
  • 安装 keepalived ;
  • 配置keepalived高可用,修改主配置文件;

详细步骤

①首先创建一个对应的目录并下载keepalivedLinux中并解压:

[root@localhost]# mkdir /soft/keepalived && cd /soft/keepalived  
[root@localhost]# wget https://www.keepalived.org/software/keepalived-2.2.4.tar.gz  
[root@localhost]# tar -zxvf keepalived-2.2.4.tar.gz  
1
2
3

②进入解压后的keepalived目录并构建安装环境,然后编译并安装:

[root@localhost]# cd keepalived-2.2.4  
[root@localhost]# ./configure --prefix=/soft/keepalived/  
[root@localhost]# make && make install  
1
2
3

③进入安装目录的/soft/keepalived/etc/keepalived/并编辑配置文件:

[root@localhost]# cd /soft/keepalived/etc/keepalived/  
[root@localhost]# vi keepalived.conf  
1
2

④编辑主机的keepalived.conf核心配置文件,如下:

global_defs {  
    # 自带的邮件提醒服务,建议用独立的监控或第三方SMTP,也可选择配置邮件发送。
    notification_email {  
        root@localhost  
    }  
    notification_email_from root@localhost  
    smtp_server localhost  
    smtp_connect_timeout 30  
    # 高可用集群主机身份标识(集群中主机身份标识名称不能重复,建议配置成本机IP)  
 router_id 192.168.12.129   
}  
  
# 定时运行的脚本文件配置  
vrrp_script check_nginx_pid_restart {  
    # 之前编写的nginx重启脚本的所在位置  
 script "/soft/scripts/keepalived/check_nginx_pid_restart.sh"   
    # 每间隔3秒执行一次  
 interval 3  
    # 如果脚本中的条件成立,重启一次则权重-20  
 weight -20  
}  
  
# 定义虚拟路由,VI_1为虚拟路由的标示符(可自定义名称)  
vrrp_instance VI_1 {  
    # 当前节点的身份标识:用来决定主从(MASTER为主机,BACKUP为从机)  
 state MASTER  
    # 绑定虚拟IP的网络接口,根据自己的机器的网卡配置  
 interface ens33   
    # 虚拟路由的ID号,主从两个节点设置必须一样  
 virtual_router_id 121  
    # 填写本机IP  
 mcast_src_ip 192.168.12.129  
    # 节点权重优先级,主节点要比从节点优先级高  
 priority 100  
    # 优先级高的设置nopreempt,解决异常恢复后再次抢占造成的脑裂问题  
 nopreempt  
    # 组播信息发送间隔,两个节点设置必须一样,默认1s(类似于心跳检测)  
 advert_int 1  
    authentication {  
        auth_type PASS  
        auth_pass 1111  
    }  
    # 将track_script块加入instance配置块  
    track_script {  
        # 执行Nginx监控的脚本  
  check_nginx_pid_restart  
    }  
  
    virtual_ipaddress {  
        # 虚拟IP(VIP),也可扩展,可配置多个。
  192.168.12.111  
    }  
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53

⑤克隆一台之前的虚拟机作为从(备)机,编辑从机的keepalived.conf文件,如下:

global_defs {  
    # 自带的邮件提醒服务,建议用独立的监控或第三方SMTP,也可选择配置邮件发送。
    notification_email {  
        root@localhost  
    }  
    notification_email_from root@localhost  
    smtp_server localhost  
    smtp_connect_timeout 30  
    # 高可用集群主机身份标识(集群中主机身份标识名称不能重复,建议配置成本机IP)  
 router_id 192.168.12.130   
}  
  
# 定时运行的脚本文件配置  
vrrp_script check_nginx_pid_restart {  
    # 之前编写的nginx重启脚本的所在位置  
 script "/soft/scripts/keepalived/check_nginx_pid_restart.sh"   
    # 每间隔3秒执行一次  
 interval 3  
    # 如果脚本中的条件成立,重启一次则权重-20  
 weight -20  
}  
  
# 定义虚拟路由,VI_1为虚拟路由的标示符(可自定义名称)  
vrrp_instance VI_1 {  
    # 当前节点的身份标识:用来决定主从(MASTER为主机,BACKUP为从机)  
 state BACKUP  
    # 绑定虚拟IP的网络接口,根据自己的机器的网卡配置  
 interface ens33   
    # 虚拟路由的ID号,主从两个节点设置必须一样  
 virtual_router_id 121  
    # 填写本机IP  
 mcast_src_ip 192.168.12.130  
    # 节点权重优先级,主节点要比从节点优先级高  
 priority 90  
    # 优先级高的设置nopreempt,解决异常恢复后再次抢占造成的脑裂问题  
 nopreempt  
    # 组播信息发送间隔,两个节点设置必须一样,默认1s(类似于心跳检测)  
 advert_int 1  
    authentication {  
        auth_type PASS  
        auth_pass 1111  
    }  
    # 将track_script块加入instance配置块  
    track_script {  
        # 执行Nginx监控的脚本  
  check_nginx_pid_restart  
    }  
  
    virtual_ipaddress {  
        # 虚拟IP(VIP),也可扩展,可配置多个。
  192.168.12.111  
    }  
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53

⑥新建scripts目录并编写Nginx的重启脚本,check_nginx_pid_restart.sh

[root@localhost]# mkdir /soft/scripts /soft/scripts/keepalived  
[root@localhost]# touch /soft/scripts/keepalived/check_nginx_pid_restart.sh  
[root@localhost]# vi /soft/scripts/keepalived/check_nginx_pid_restart.sh  
  
#!/bin/sh  
# 通过ps指令查询后台的nginx进程数,并将其保存在变量nginx_number中  
nginx_number=`ps -C nginx --no-header | wc -l`  
# 判断后台是否还有Nginx进程在运行  
if [ $nginx_number -eq 0 ];then  
    # 如果后台查询不到`Nginx`进程存在,则执行重启指令  
    /soft/nginx/sbin/nginx -c /soft/nginx/conf/nginx.conf  
    # 重启后等待1s后,再次查询后台进程数  
    sleep 1  
    # 如果重启后依旧无法查询到nginx进程  
    if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then  
        # 将keepalived主机下线,将虚拟IP漂移给从机,从机上线接管Nginx服务  
        systemctl stop keepalived.service  
    fi  
fi  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

⑦编写的脚本文件需要更改编码格式,并赋予执行权限,否则可能执行失败:

[root@localhost]# vi /soft/scripts/keepalived/check_nginx_pid_restart.sh  
  
:set fileformat=unix # 在vi命令里面执行,修改编码格式  
:set ff # 查看修改后的编码格式  
  
[root@localhost]# chmod +x /soft/scripts/keepalived/check_nginx_pid_restart.sh  
1
2
3
4
5
6

⑧由于安装keepalived时,是自定义的安装位置,因此需要拷贝一些文件到系统目录中:

[root@localhost]# mkdir /etc/keepalived/  
[root@localhost]# cp /soft/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/  
[root@localhost]# cp /soft/keepalived/keepalived-2.2.4/keepalived/etc/init.d/keepalived /etc/init.d/  
[root@localhost]# cp /soft/keepalived/etc/sysconfig/keepalived /etc/sysconfig/  
1
2
3
4

⑨将keepalived加入系统服务并设置开启自启动,然后测试启动是否正常:

[root@localhost]# chkconfig keepalived on  
[root@localhost]# systemctl daemon-reload  
[root@localhost]# systemctl enable keepalived.service  
[root@localhost]# systemctl start keepalived.service  
1
2
3
4

其他命令:

systemctl disable keepalived.service # 禁止开机自动启动  
systemctl restart keepalived.service # 重启keepalived  
systemctl stop keepalived.service # 停止keepalived  
tail -f /var/log/messages # 查看keepalived运行时日志  
1
2
3
4

⑩最后测试一下VIP是否生效,通过查看本机是否成功挂载虚拟IP

[root@localhost]# ip addr  
1

从上图中可以明显看见虚拟IP已经成功挂载,但另外一台机器192.168.12.130并不会挂载这个虚拟IP,只有当主机下线后,作为从机的192.168.12.130才会上线,接替VIP

最后测试一下外网是否可以正常与VIP通信,即在Windows中直接ping VIP。外部通过VIP通信时,也可以正常Ping通,代表虚拟IP配置成功。

经过上述步骤后,keepalivedVIP机制已经搭建成功,在上个阶段中主要做了几件事:

  • 一、为部署Nginx的机器挂载了VIP
  • 二、通过keepalived搭建了主从双机热备。
  • 三、通过keepalived实现了Nginx宕机重启。

# Nginx高可用性测试

# 1)宕机重启

①由于前面没有域名的原因,因此最初server_name配置的是当前机器的IP,所以需稍微更改一下nginx.conf的配置:

sever{  
    listen    80;  
    # 这里从机器的本地IP改为虚拟IP  
 server_name 192.168.12.111;  
 # 如果这里配置的是域名,那么则将域名的映射配置改为虚拟IP  
}
1
2
3
4
5
6

②最后来实验一下效果:

在上述过程中,首先分别启动了keepalived、nginx服务,然后通过手动停止nginx的方式模拟了Nginx宕机情况,过了片刻后再次查询后台进程,我们会发现nginx依旧存活。从这个过程中不难发现,keepalived已经为我们实现了Nginx宕机后自动重启的功能。

# 2)服务器故障

①再模拟一下服务器出现故障时的情况:

在上述过程中,我们通过手动关闭keepalived服务模拟了机器断电、硬件损坏等情况(因为机器断电等情况=主机中的keepalived进程消失),然后再次查询了一下本机的IP信息,很明显会看到VIP消失了!

②现在再切换到另外一台机器:192.168.12.130来看看情况:

此刻我们会发现,在主机192.168.12.129宕机后,VIP自动从主机飘移到了从机192.168.12.130上,而此时客户端的请求就最终会来到130这台机器的Nginx上。

# 3)测试结果

最终,利用Keepalived对Nginx做了主从热备之后,无论是遇到线上宕机还是机房断电等各类故障时,都能够确保应用系统能够为用户提供7x24小时服务。

# 十六、Nginx性能优化

# 1. 开启长连接

优化一:打开长连接配置

通常Nginx作为代理服务,负责分发客户端的请求,那么建议开启HTTP长连接,用于减少握手的次数,降低服务器损耗。具体如下:

upstream xxx {  
    # 长连接数  
    keepalive 32;  
    # 每个长连接提供的最大请求数  
    keepalived_requests 100;  
    # 每个长连接没有新的请求时,保持的最长时间  
    keepalive_timeout 60s;  
}  
1
2
3
4
5
6
7
8

# 2. 开启零拷贝技术

零拷贝这个概念,在大多数性能较为不错的中间件中都有出现,例如Kafka、Netty等。

Nginx中也可以配置数据零拷贝技术,如下:

sendfile on; # 开启零拷贝机制  
1

零拷贝读取机制与传统资源读取机制的区别:

  • 传统方式: 硬件-->内核-->用户空间-->程序空间-->程序内核空间-->网络套接字
  • 零拷贝方式: 硬件-->内核-->程序内核空间-->网络套接字

从上述这个过程对比,很轻易就能看出两者之间的性能区别。

# 3. 开启多包共发机制

开启无延迟或多包共发机制

Nginx中有两个较为关键的性能参数,即tcp_nodelay、tcp_nopush,开启方式如下:

tcp_nodelay on;  
tcp_nopush on;  
1
2

# 1)tcp_nodelay

TCP/IP协议中默认是采用了Nagle算法的,即在网络数据传输过程中,每个数据报文并不会立马发送出去,而是会等待一段时间,将后面的几个数据包一起组合成一个数据报文发送,但这个算法虽然提高了网络吞吐量,但是实时性却降低了。

因此如果你的项目属于交互性很强的应用,那么可以手动开启tcp_nodelay配置,让应用程序向内核递交的每个数据包都会立即发送出去。但这样会产生大量的TCP报文头,增加很大的网络开销。

# 2)tcp_nopush

相反,有些项目的业务对数据的实时性要求并不高,追求的则是更高的吞吐,那么则可以开启tcp_nopush配置项,这个配置就类似于“塞子”的意思,首先将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去。设置该选项后,内核会尽量把小数据包拼接成一个大的数据包(一个MTU)再发送出去。

当然若一定时间后(一般为200ms),内核仍然没有积累到一个MTU的量时,也必须发送现有的数据,否则会一直阻塞。

# 总结

tcp_nodelay、tcp_nopush两个参数是“互斥”的。

  • 如果追求响应速度的应用推荐开启tcp_nodelay参数,如IM、金融等类型的项目。
  • 如果追求吞吐量的应用则建议开启tcp_nopush参数,如调度系统、报表系统等。

注意:

  • tcp_nodelay一般要建立在开启了长连接模式的情况下使用。
  • tcp_nopush参数是必须要开启sendfile参数才可使用。

# 4. 调整Worker工作进程

Nginx启动后默认只会开启一个Worker工作进程处理客户端请求。

而我们可以根据机器的CPU核数开启对应数量的工作进程,以此来提升整体的并发量支持,如下:

# 自动根据CPU核心数调整Worker进程数量  
worker_processes auto;  
1
2

工作进程的数量最高开到8个就OK了,8个之后就不会有再大的性能提升。

同时也可以稍微调整一下每个工作进程能够打开的文件句柄数

# 每个Worker能打开的文件描述符,最少调整至1W以上,负荷较高建议2-3W  
worker_rlimit_nofile 20000;  
1
2

操作系统内核(kernel)都是利用文件描述符来访问文件,无论是打开、新建、读取、写入文件时,都需要使用文件描述符来指定待操作的文件。

因此该值越大,代表一个进程能够操作的文件越多(但不能超出内核限制,最多建议3.8W左右为上限)。

# 5. 开启CPU亲和机制

对于并发编程较为熟悉的伙伴都知道,因为进程/线程数往往都会远超出系统CPU的核心数,因为操作系统执行的原理本质上是采用时间片切换机制,也就是一个CPU核心会在多个进程之间不断频繁切换,造成很大的性能损耗。

CPU亲和机制则是指将每个Nginx的工作进程,绑定在固定的CPU核心上,从而减小CPU切换带来的时间开销和资源损耗,开启方式如下:

worker_cpu_affinity auto;  
1

# 6. 开启epoll模型

开启epoll模型及调整并发连接数

Nginx、Redis都是基于多路复用模型去实现的程序。但最初版的多路复用模型select/poll最大只能监听1024个连接,而epoll则属于select/poll接口的增强版,因此采用该模型能够大程度上提升单个Worker的性能,如下:

events {  
    # 使用epoll网络模型  
    use epoll;  
    # 调整每个Worker能够处理的连接数上限  
    worker_connections  10240;  
}  
1
2
3
4
5
6

# 其他优化

其实在前面就谈到的动静分离、分配缓冲区、资源缓存、防盗链、资源压缩等内容,也都可归纳为性能优化的方案。

# 十七、Nginx FAQ

# Vue项目部署路径报错404

Q:Vue项目打包部署在Nginx,请求IP:端口/router_path,报错404.

W:

Nginx的nginx.conf文件配置错误。

如下进行配置:

events {
    worker_connections  1024;
}

http {
    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html/;
            try_files $uri $uri/ /index.html;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

首先通过root指定了Vue应用的根路径。然后,try_files指令尝试按顺序查找请求的资源:

  • $uri:尝试匹配所请求的文件。
  • $uri/:尝试匹配请求的目录。
  • /index.html:如果上述尝试都没有匹配项,则重定向到index.html文件。

这个配置适用于使用Vue Router的history模式,确保所有的路由都被正确处理,因为无论请求的是哪个路由,都会返回index.html,然后Vue Router将根据实际路由进行处理。

# 十八、Nginx相关资料