前端抢饭碗系列之深入Nginx

  在大部分童鞋的印象里,Nginx应该都是属于后台工作的范畴,前端只要写好页面就好了;然后随着大前端范围的不断扩展,前端也在不断的进军服务器领域,而Nginx就是进军服务器领域必备的技能之一;以前我们都需要“低声下气”的让后端的同事给我们配置页面域名,但是学会了Nginx配置,域名配置、代理转发什么的完全就可以我们自己来了,这样抢来的饭碗它。。。。它难道不香吗?

简介

  那么Nginx到底是什么,首先我们来看一下百度百科对Nginx的定义:

Nginx是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。

  这里有三个词很关键,我们来拆解一下,分别是是高性能、反向代理和web服务器;首先这个web服务器自不用多说,像我们熟知的Apache、IIS、Tomcat等都是web服务器;然后是高性能,一个服务器的性能自然是网站开发者最为关心的,那么服务器的性能如何来进行衡量呢?一般可以通过CPU和内存的使用量来进行衡量。经过笔者简单的并发测试,在20000个并发链接时,CPU和内存占用也非常低,CPU仅占5%,内存占用也才2MB不到。

  我们可以通过一个web压力测试工具Apache Bench,对Nginx进行简单的压力测试;通过在命令行ab -n 20000 -c 10000 [url],我们对Nginx的首页发起请求总数为20000,并发数为10000的请求测试,测试结果如下:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

压力测试结果

  我们看到总的请求时间(Time taken for tests)是25秒,平均每个请求耗时(Time per request)1.25毫秒,在这么高的并发量下面,服务器响应性能还是挺不错的。

  然后是反向代理,与之对应的就是正向代理,这两者的区别也是面试中经常被问到的。我们先来看一下什么是正向代理,一个正向代理最典型的例子就是我们常用的“梯子”。

表情包

  我们直接访问Google,是访问不到的,但是如果我们使用了代理服务器,那么通过访问代理服务器就可以浏览Google,这里的代理服务器就属于正向代理;通过正向代理我们可以访问原来无法访问的资源。

正向代理

  那么什么是反向代理呢?反向代理最典型的例子就是我们的Nginx服务器了;比如我们在访问某个网站时,由代理服务器去目标服务器获取数据后返回给客户端,这样就能够隐藏真实服务器的IP地址,只对外开放代理服务器,以防止外网对内网服务器的恶性攻击。

反向代理

  理解了上面两个典型的案例,相信大家对正向反向代理也了解了,我们总结一下:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  • 正向代理,代理客户端,服务端不知道实际发起请求的客户端。
  • 反向代理,代理服务端,客户端不知道实际提供服务的服务端。

安装配置

  Nginx安装程序分为Linux版和Windows版,Windows版本的Nginx下载解压后就可以直接运行了,而Linux版本的需要make、configure等命令编译安装,好处是可以方便灵活的编译不同的模块到Nginx;网上也有很多的安装教程,这里就不再赘述了,可以从官网下载适合自己的版本,下载好后我们来看一下他的目录结构:

1
2
3
4
5
6
7
8
├── conf            #所有配置文件的目录
├── nginx.conf #主配置文件
├── mime.types #媒体类型控制文件
├── contrib #存放一些实用工具
├── docs #文档资料
├── html #默认解析的静态文件目录
├── logs #日志目录
├── sbin #启动运行程序

  我们经常用到的就是conf目录和html目录;而在根目录可以运行常用的一些命令对Nginx进行操作控制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
nginx -s reopen 	#重启Nginx
nginx -s reload #重新加载Nginx配置文件,然后以优雅的方式重启Nginx
nginx -s stop #强制停止Nginx服务
nginx -s quit #优雅地停止Nginx服务(即处理完所有请求后再停止服务)
nginx -h #打开帮助信息
nginx -v #显示版本信息并退出
nginx -V #显示版本和配置选项信息,然后退出
nginx -t #检测配置文件是否有语法错误,然后退出
nginx -T #检测配置文件是否有语法错误,转储并退出
nginx -q #在检测配置文件期间屏蔽非错误信息
nginx -p prefix #设置前缀路径(默认是:/usr/share/nginx/)
nginx -c filename #设置配置文件(默认是:/etc/nginx/nginx.conf)
nginx -g directives #设置配置文件外的全局指令
killall nginx #杀死所有nginx进程

  我们看前四个命令会发现,这四个命令可以分为两种,重启和停止Nginx,不过一种是强制的方式,另一种是优雅的方式;强制的方式就是让Nginx立即停止当前处理的所有请求,丢弃链接,停止工作;而优雅的方式是允许Nginx将当前正在处理的请求处理完成,但是不再接收新的请求,所有处理完成后再停止工作。

  我们再来看一下主要配置文件nginx.conf的基本结构:

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
# nginx进程数,建议设置为等于CPU总核心数
worker_processes 1;
# 进程文件
pid logs/nginx.pid;
# 单个进程最大连接数
events {
worker_connections 1024;
}
http {
# 文件扩展名与类型映射表
include mime.types;
# 默认文件类型
default_type application/octet-stream;
# 开启gzip压缩
gzip on;
sendfile on;
keepalive_timeout 65;
server {
# 监听端口
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
}
}

  配置文件中主要可以分为以下几个块:

    谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  • 全局模块:从配置文件开始到events块之间的内容,此处的配置影响nginx服务器整体的运⾏,⽐如worker进程的数量、错误⽇志的位置等
  • events:配置影响nginx服务器或与用户的网络连接。
  • http:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。
  • server:配置虚拟主机的相关参数,一个http中可以有多个server。
  • location:配置请求的路由,以及各种页面的处理情况。

  很多时候,我们不会将所有的配置全都写在一个主配置文件,因为这样会显得冗长,也不知道每个模块是做什么用的;而是会根据项目来拆分多个配置文件,每个配置文件彼此独立,互不干扰,然后在主配置文件中引入;我们在conf目录下新建一个projects目录,然后可以新建多个.conf配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# /conf/projects/home.conf
server {
listen 8080;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
}
server {
listen 8081;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
}

  然后在主配置nginx.conf中将projects目录下的所有配置文件引入:

1
2
3
4
5
6
7
8
9
http {
include mime.types;
default_type application/octet-stream;
gzip on;
sendfile on;
keepalive_timeout 65;
## 引入projects目录下所有的配置文件
include projects/*.conf;
}

  这样我们可以直接在projects目录下新增.conf后缀的配置文件,而不用修改主配置文件;但是我们修改完还不能确定是否会有错误,可以通过命令对配置文件进行检测:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
nginx -t
#nginx: the configuration file nginx/conf/nginx.conf syntax is ok
#nginx: configuration file nginx/conf/nginx.conf test is successful

  通过检测发现没有任何报错,就可以优雅的重启服务器了:

1
nginx -s reload

静态服务器

  作为一个web服务器,最重要的就是能够对静态资源提供访问服务,我们的Nginx服务器可以用来托管一些静态的资源,比如js、css、图片等,访问某一特定的静态资源路径时会转发到本地目录文件上;那么我们就来看Nginx是如何一步一步的通过域名配置、URI配置以及目录配置来命中请求的。

Nginx首页

server_name配置

  在上面的配置中,我们主要是将server_name设置为localhost,但是这样仅能让局域网内的主机访问到;我们想要让广域网上的其他主机访问,可以将server_name匹配域名,它的参数值可以是以下几种:

  在上面正则表达式中,^表示以www开头,紧跟一个或多个数字(\d+),然后跟上域名my.com,最后以$结尾;因此上面的表达式可以匹配的域名比如www1.my.com,但是www.my.com就不行。

  正则表达式还支持字符串捕获功能,即将正则表达式匹配成功的名称中的一部分字符串截取出来,放在变量中供后面使用;比如将server_name进行如下设置:

1
2
3
4
5
6
7
8
server {
listen 80;
server_name ~^(.+)?\.my\.com$;
location / {
root /usr/share/nginx/html/$1;
index index.html index.htm;
}
}

  这样,通过二级域名home.my.com到达Nginx时,被server_name正则表达式捕获,将其中的home字符串存入$1变量中,我们在/usr/share/nginx/html/home目录下的静态资源就能通过home.my.com域名来访问了;我们服务器的目录就可以是这样的:

1
2
3
4
5
6
7
8
9
/usr/share/nginx/html/
|- home
|- index.html
|- blog
|- index.html
|- mail
|- index.html
|- photo
|- index.html

  这样就只需要一个server块来完成多个站点的配置。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  nginx允许一个虚拟主机有多个域名,因此我们可以给server_name同时配置多个域名,多个之间以空格分隔:

1
2
3
4
5
server {
listen 80;
server_name a.com b.com c.com;
# ...其他配置
}

  由于server_name支持以上三种配置方式,如果出现多个server块同时匹配了相同的域名,那么这个请求交给哪个server呢?因此优先级顺序如下:

  1. 精确匹配server_name
  2. 通配符在开始时匹配server_name
  3. 通配符在结尾时匹配server_name
  4. 正则表达式匹配server_name

  如果我们想让局域网内的设备访问nginx,可以将server_name设置ip地址的方式:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
4
server {
listen 80;
server_name localhost 192.168.1.101;
}

  如果还不能访问,可以查看下是否是防火墙的原因,在防火墙允许通过的应用中将Nginx勾选(没有找到Nginx可以点击允许其他应用进行新增):

Windows防火墙

  有时候我们还会见到将server_name设置为_(下划线),意味着server_name为空,即匹配全部的主机;我们可以配置host,将a.com、b.com和c.com都指向本机,然后配置nginx:

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

  这样我们不仅可以通过域名a.com、b.com、c.com来访问,也能通过ip的方式。

location配置

  location用于匹配不同的URI请求,它的语法如下:

1
2
location [ = | ~ | ~* | ^~ ] uri { ... }
location @/name/ { … }

  这里的uri就是待匹配的请求字符串,可以是不含正则的字符串,比如/home,称为标准URI;也可以是包含正则的字符串,比如\.html$(表示以.html结尾),称为正则URI。而方括号中的四种匹配符都是可选的,用来改变请求字符串与URI的匹配方式,我们来看下四种匹配符的解释:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

匹配符 解释
不填 location后没有参数,直接跟着标准URI,表示前缀匹配,代表跟请求中的URI从头开始匹配
= 用于标准URI前,要求请求字符串与其精准匹配,成功则立即处理,nginx停止搜索其他匹配
^~ 用于标准URI前,要求一旦匹配就会立即处理,不再去匹配其他正则URI,一般用来匹配目录
~ 用于正则URI前,表示URI包含正则表达式,区分大小写
~* 用于正则URI前,表示URI包含正则表达式,不区分大小写
@ 定义一个命名的location,@定义的location名字一般用在内部定向

  我们来看下每种匹配规则能匹配的url,首先不填代表的话表示前缀匹配,如果我们有多个相似的前缀匹配:

1
2
3
4
5
6
location /pre/fix {
# ...
}
location /pre {
# ...
}

  对于请求/pre/fix/home,根据最大匹配原则,匹配第一个location。

  然后是=,要求路径完全匹配:

1
2
3
4
5
6
7
8
location = /abc {
# ...
}

# /abc 匹配
# /abcde 不匹配
# /abc/ 不匹配,带有结尾的/
# /cde/abc不匹配

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  其次是^~最佳匹配,它的优先级高于正则表达式:

1
2
3
4
5
6
7
8
location ^~ /login {
# ...
}

# /login 匹配
# /loginss 匹配
# /login/ 匹配
# /home/login 不匹配

  接着是~正则表达式匹配,它区分大小写匹配(注意:windows版本nginx不区分):

1
2
3
4
5
6
7
location ~ \.(gif|jpg|png|js|css)$ {
# ...
}
# /bg.png 匹配
# /bg.PNG 不匹配
# /bg.png?a=1 匹配
# /bg.jpeg 不匹配

  ~*同样也是正则匹配,只不过它不区分大小写,这里就不再演示。

  如果我们的URI匹配到了多个location,其并不完全按照在配置文件中出现的顺序来进行匹配,URI会按照如下规则进行匹配:

  1. = 精确匹配会第一个被处理。如果发现精确匹配,nginx停止搜索其他匹配。
  2. 普通字符匹配,正则表达式规则和长的块规则将被优先和查询匹配,也就是说如果该项匹配还需去看有没有正则表达式匹配和更长的匹配。
  3. ^~ 则只匹配该规则,nginx停止搜索其他匹配,否则nginx会继续处理其他location指令。
  4. 最后匹配理带有~~*的指令,如果找到相应的匹配,则nginx停止搜索其他匹配;当没有正则表达式或者没有正则表达式被匹配的情况下,那么匹配程度最高的逐字匹配指令会被使用。
  5. 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

请求目录配置

  在location匹配URI后,就需要在服务器指定的目录中寻找请求资源,而rootalias就是用来指定目录的两种指令,两者主要的区别在于如何解析location后面的路径;我们首先来看下root的用法,假如我们需要将/data/下面的所有路径转发到html/roottest下面:

1
2
3
location /data/ {
root html/roottest;
}

  当location接收到/data/index.html的请求时,会在html/roottest/data/目录下找到index.html文件并进行相应,root会将root路径和location路径进行拼接。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  而alias指令则改变location接收到的请求路径,假如我们需要将/data1/下面的所有路径转发到html/aliastest下面:

1
2
3
location /data1/ {
alias html/aliastes/;
}

  当location接收到/data1/index.html的请求时,会在html/aliastes/目录下查找index.html文件并响应。

需要注意的是:alias指令后面的路径必须以/结束,否则会找不到文件,而root则可有可无。

访问权限控制

  针对一些静态资源,我们可能会设置一些用户访问权限,比如和js一起打包产出的.map文件,会对源码进行映射;但是我们想让它只能针对公司的ip进行开放,对外网的ip禁止访问,这时就需要用到allowdeny命令了。

  假如局域网还有两个设备,我们只能让这两个设备的ip通过访问:

1
2
3
4
5
6
location / {
alias html/aliastes/;
allow 192.168.1.102;
allow 192.168.1.103;
deny all;
}

  deny和allow指令是由ngx_http_access_module模块提供,Windows版本的Nginx并不包含该模块。

  还可以对前端的.map文件进行访问权限控制,打包后的map文件一般会放在服务器上,但是如果能对所有人开放,别人就能查看到对应源码;因此我们可以控制只有公司的ip才有访问权限:

try_files

  前端在配置路由时经常会用到history路由模式,因此后台就需要映射对应的路由到index.html;但是如果我们给每个路由都配置一个location就会比较繁琐,因此可以通过try_files指令来进行尝试解析;try_files的语法规则如下:

1
2
3
4
# 格式1:
try_files file ... uri;
# 格式2:
try_files file ... =code;

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  假设我们打包出来的单页面位于/html/my/index.html,我们想要将/login、/regisrer等路由指向index.html,我们可以配置try_files:

1
2
3
4
5
6
7
server {
listen 8080;
server_name localhost;
location / {
try_files $uri /my/index.html;
}
}

  对于多页面的应用,假设我们的页面都放在/html/pages/目录下,我们想要访问/login时响应/html/pages/login.html页面,可以通过$uri

1
2
3
4
5
6
7
8
9
server {
listen 8080;
server_name localhost;
location / {
index index.html index.htm;
root html/pages;
try_files $uri /$uri.html $uri/index.html /index.html;
}
}

  这里我们设置root目录为html/pages,当我们访问/login路由时,这里的$uri就是/login,try_files会去尝试在根目录下找/login.html;如果找不到就尝试/login/index.html,最后找不到则会默认返回index.html。

gzip

  我们都知道在服务端开启gzip压缩能够使得js、css、html等文件在传输时大幅提高访问速度,优化网站性能;gzip压缩后的文件大小可以变为原来的30%甚至更小;而对于图片、视频、音频等其他多媒体文件,因为压缩效果不好,所以不会开启压缩。

  gzip压缩本质上是服务器端压缩,传输到浏览器后解压解析,我们来看下gzip的原理示意:

Gzip原理

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  可以看到在请求和相应头上分别加了accept-encoding和content-encoding来进行传输;我们可以通过一个js的请求数据来查看:

Gzip请求响应头

  既然gzip有这么多的好处,我们来看下nginx如何进行配置,gzip的配置可以在http块或者server块中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 开启gzip
gzip on;
# 设置gzip申请内存的大小
gzip_buffers 32 4K;
# 设置gzip压缩等级
# 压缩级别 1-9,级别越高压缩率越大但耗CPU
gzip_comp_level 6;
# 正则匹配User-Agent中的值,匹配上则不进行gzip
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
# 设置允许压缩的页面最小字节数
gzip_min_length 1024;
# 设定进行gzip压缩的最小http版本
gzip_http_version 1.0;
# 需要压缩哪些响应类型的资源
gzip_types application/javascript text/css text/xml;
# 添加“Vary: Accept-Encoding”响应头
gzip_vary on;

密码控制

  对于一些简单的页面,我们想要通过密码来限制其他用户的访问,但是又不想接入复杂的账号体系,Nginx提供了简单的账号密码控制;首先我们通过Linux的工具创建一个密码本存放账号密码:

1
2
sudo yum install httpd-tools -y
sudo htpasswd -c passwd/passwd admin

  passwd/passwd文件就是生成的密码文件,运行后会要求连续两次输入密码,成功后为admin用户添加了密码;然后我们就修改nginx的配置文件,对站点开启密码验证:

1
2
3
4
5
6
7
8
9
server {
listen 8000;
server_name localhost;
auth_basic "请输入账号密码";
auth_basic_user_file /etc/nginx/conf/passwd/passwd;
location / {
# .....
}
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  重启nginx,再次访问站点就会出现需要身份验证的弹框了。

反向代理

  上面我们介绍了正向代理和反向代理的区别,反向代理功能是nginx的三大主要功能之一(静态web服务器、反向代理、负载均衡)。反向代理不需要额外的模块,默认自带proxy_pass和fastcgi_pass指令,通过在location块中配置即可实现:

1
2
3
4
5
6
7
server {
listen 80;
server_name a.com;
location / {
proxy_pass http://192.168.1.102:8080;
}
}

  在配置proxy_pass时,我们需要注意url后面的/`;当我们通过下面几种情况访问/proxy/home.html``时:

1
2
3
location /proxy/ {
proxy_pass http://192.168.1.102:8080/;
}

  第一种情况url后面带上/,则会被代理到http://192.168.1.102:8080/home.html

1
2
3
location /proxy/ {
proxy_pass http://192.168.1.102:8080;
}

  第二种情况url后不带/,则会被代理到http://192.168.1.102:8080/proxy/home.html

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
location /proxy/ {
proxy_pass http://192.168.1.102:8080/doc/;
}

  第三种情况代理/doc/,则会被代理到http://192.168.1.102:8080/doc/home.html

1
2
3
location /proxy/ {
proxy_pass http://192.168.1.102:8080/doc;
}

  第四种情况代理/doc,则会被代理到http://192.168.1.102:8080/dochome.html

  在配置反向代理时,我们还可以修改代理请求的请求参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
location /proxy/ {
proxy_pass http://192.168.1.102:8080/;
# 修改请求的method
proxy_method GET;
# 修改请求的http协议版本
proxy_http_version 1.1;
# 将原来host字段放到转发请求中
proxy_set_header Host $host;
#获取真实ip
proxy_set_header X-Real-IP $remote_addr;
# 代理服务器每成功收到一个请求,就把请求来源IP地址添加到右边
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  经过反向代理后,由于客户端和web服务器之间增加了一个代理层,因此web服务器无法拿到客户端请求的host和真实ip,我们通过proxy_set_header指令修改代理请求的头部;$host和$remote_addr是用户真实的host和ip,这里作为变量传入Host和X-Real-IP字段,因此我们在客户端服务器想要获取真实ip就可以通过request.getAttribute(“X-real-ip”)的方式。

负载均衡

  随着互联网的发展,用户规模的增加,服务器的压力也越来越大,如果只使用一台服务器有时候不能承受流量的压力,这时我们就需要将部分流量分散到多台服务器上,使得每台服务器都均衡的承担压力。

  nginx负载均衡目前支持六种策略:轮询策略、加权轮询策略、ip_hash策略、url_hash策略、fair策略和sticky策略;六种策略可以分为两大类,内置策略(轮询、加权轮询、ip_hash)和扩展策略(url_hash、fair、sticky);默认情况下内置策略自动编译在Nginx中,而扩展策略需要额外安装。

  既然是负载,那么我们需要启用多台服务器;这里为了方便演示,我们在一台电脑上运行node脚本来模拟3台服务器;同时为了方便看到每台服务器有多少流量,每访问一次就计数一次:

1
2
3
4
5
6
7
8
9
10
const express = require("express");
const app = express();
const PORT = 8080;
const path = require("path");
let count = 0;
app.get("*", (req, res) => {
count++;
res.sendFile(path.resolve(__dirname, "./index.html"));
});
app.listen(PORT);

  然后我们修改端口号,这样我们就有8080、8081、8082三个服务器了。

轮询策略

  轮询策略,顾名思义,就是按照请求顺序,逐一分配到不同的服务器节点;如果某台服务器出现问题,会自动剔除。

1
2
3
4
5
6
7
8
9
10
11
12
upstream myserver {
server 192.168.1.101:8080;
server 192.168.1.101:8081;
server 192.168.1.101:8082;
}
server {
listen 8070;
server_name _;
location / {
proxy_pass http://myserver;
}
}

  我们还是通过测试工具Apache Bench来并发100个请求到Nginx:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
ab -n 100 -c 10 http://localhost:8070/

  最后统计每台服务器的结果,每台服务器的请求还是很平均的:

1
2
3
8080:34个请求
8081:33个请求
8082:33个请求

加权轮询策略

  加权轮询在基本轮询策略上考虑各服务器节点接受请求的权重,指定服务器节点被轮询的权重,主要用于服务器节点性能不均的情况。

  通过在server节点后配置weight来设置权重,weight的大小和访问比率成正比(weight的默认值为1);我们给三台服务器设置访问比是1:3:2

1
2
3
4
5
upstream myserver {
server 192.168.1.101:8080;
server 192.168.1.101:8081 weight=3;
server 192.168.1.101:8082 weight=2;
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  压力测试后统计服务器的请求结果,和我们配置的比率还是几乎相同的:

1
2
3
808016个请求
808151个请求
808233个请求

注:由于weight是内置,所以可以直接和其他策略配合使用。

ip_hash策略

  ip_hash策略是将前端访问的ip进行hash操作后,然后根据hash的结果将请求分配到不同的节点上,这样使得每个ip都会固定访问服务节点;这样做的好处是用户的session只在一个后端服务器节点上,不必考虑一个session存在多台服务器节点出现session共享问题。

1
2
3
4
5
6
upstream myserver {
ip_hash;
server 192.168.1.101:8080;
server 192.168.1.101:8081 weight=3;
server 192.168.1.101:8082 weight=2;
}

  压力测试后统计服务器的请求结果,我们发现所有的请求都到固定一台服务器上了:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
80800个请求
80810个请求
8082100个请求

url_hash策略

  url_hash策略是将url地址进行hash操作,根据hash结果请求定向到同一服务器节点上;url_hash的优点是能够提高后端缓存服务器的效率。

1
2
3
4
5
6
upstream myserver {
hash $request_uri;
server 192.168.1.101:8080;
server 192.168.1.101:8081;
server 192.168.1.101:8082;
}

  压力测试后统计服务器的请求结果:

1
2
3
80800个请求
80810个请求
8082100个请求

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  如果我们切换不同的url,/home、/list等,都会分配到不同的服务器节点。

fair策略

  fair策略请求转发到负载最小的后端服务器节点上。Nginx通过服务器节点对响应时间来判断负载情况,响应时间最短的节点负载就相对较轻,Nginx就会将前端请求转发到此服务器节点上。

注:fair策略默认不被编译进nginx内核,需要额外安装

1
2
3
4
5
6
upstream myserver {
fair;
server 192.168.1.101:8080;
server 192.168.1.101:8081;
server 192.168.1.101:8082;
}

  压力测试后统计服务器的请求结果:

1
2
3
808033个请求
808133个请求
808234个请求

sticky策略

  sticky策略是基于cookie的一种负载均衡解决方案,通过分发和识别cookie,使来自同一个客户端的请求落在同一台服务器上,默认cookie标识名为route。

  sticky策略看起来和ip_hash策略类似,但是又有一定区别。假设在一个局域网内有3台电脑,他们有3个内网IP,但是他们发起请求时,却只有一个外网IP,如果使用ip_hash方式,则Nginx会将请求分配到同一服务器;如果使用sticky策略,则会把请求分配到不同服务器上,这是ip_hash无法做到的。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

注:sticky策略默认不被编译进nginx内核,需要额外安装

1
2
3
4
5
6
upstream myserver {
sticky name=sticky_cookie expires=6h;
server 192.168.1.101:8080;
server 192.168.1.101:8081;
server 192.168.1.101:8082;
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  sticky默认的cookie的名称是route,我们可以通过name修改,还有一些其他的cookie参数可以进行修改:

  • [name=route]       设置用来记录会话的cookie名称
  • [domain=.foo.bar]    设置cookie作用的域名
  • [path=/]         设置cookie作用的URL路径,默认根目录
  • [expires=1h]        设置cookie的生存期,默认不设置,浏览器关闭即失效
  • [hash=index|md5|sha1] 设置cookie中服务器的标识是用明文还是使用md5值,默认使用md5
  • [no_fallback]       设置该项,当sticky的后端机器挂了以后,nginx返回502 (Bad Gateway or Proxy Error) ,而不转发到其他服务器,不建议设置
  • [secure]         设置启用安全的cookie,需要HTTPS支持
  • [httponly]        允许cookie不通过JS泄漏,没用过

  我们通过浏览器来访问,在cookie中可以看到sticky下发的cookie

sticky策略cookie

注:由于cookie最初由服务器端下发,如果客户端禁用cookie,则cookie不会生效。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

其他参数

  upstream还有一些参数我们可以配合负载均衡:

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

参数 描述
fail_timeout 与max_fails结合使用
max_fails 设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了
fail_time 服务器会被认为停机的时间长度,默认为10s。
backup 标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里。
down 标记服务器永久停机了。
keepalive 连接数(keepalive的值)指定了每个工作进程中保留的持续连接到nginx负载均衡器缓存的最大值。如果超过这个设置值的闲置进程想链接到nginx负载均衡器组,最先连接的将被关闭。
1
2
3
4
5
6
7
8
9
10
11
12
upstream backserver{ 
ip_hash;
# down 表示单前的server暂时不参与负载
server 192.168.1.101:8080 down;
server 192.168.1.101:8081;
# max_fails允许请求失败的次数默认为1,此处允许失败的次数为3。每次失败后暂停的时间为30s
server 192.168.1.101:8082 max_fails=3 fail_timeout=30s;
# 其它所有的非backup机器down或者忙的时候,请求backup机器
server 192.168.1.101:8083 backup;
# 连接到nginx负载均衡器的最大
keepalive 16;
}

本网所有内容文字和图片,版权均属谢小飞所有,任何媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布/发表。如需转载请关注公众号【前端壹读】后回复【转载】。