1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 利用Nginx实现负载均衡(反向代理)完全详解

利用Nginx实现负载均衡(反向代理)完全详解

时间:2021-06-26 09:35:29

相关推荐

利用Nginx实现负载均衡(反向代理)完全详解

1、常见负载均衡的方式(概念普及)

【1】用户手动选择

例如我们玩游戏,服务器会显示当前服务器的状态是拥挤、繁忙、还是空闲,然后用户根据自己实际需要,选择自己想去的服务器。

如果服务器人太多(达到上限),则触发排队,比如猪场主推的一些游戏,在刚上线的时候通常都特别火,要排很久,还有戏称阴兵的存在。

这个适合用户和用户之间需要交互的情况(且跨服务器之间无法交互,或交互比较麻烦)。

【2】DNS解析控制

需要知道一个前提,你访问同一个域名,第一次可能是1xx.1xx.1xx.1,第二次可能是1xx.1xx.1xx.2(即不同的ip)。

但由于这两个都是网站的服务器,所以不管访问哪个都一样。

在我们访问域名的时候,DNS解析服务器,会将你的域名转为IP,因此我们具体访问哪一个IP上的服务器,是根据DNS解析服务器来决定的。

以阿里云为例,我们可以配置云解析。

具体来说,我们可以设置 被解析为 1xx.1xx.1xx.1 和 1xx.1xx.1xx.2 ,甚至更多。

效果是当访问 时,用户可能跳转到 1xx.1xx.1xx.1 和 1xx.1xx.1xx.2 或者其他你设置的 ip 上。

缺点:

假如 1xx.1xx.1xx.1 服务器挂了,所以被导向这个 IP 地址的流量,都不会得到相应;不是真负载均衡,特别在各个服务器高压力的情况下,部分服务器可能先达到 100% 负载,然后出现问题。

【3】涉及到网络七层协议的负载均衡

分为第四层(传输层)和第七层(网络层)两种。

第四层的负载均衡简单来说,当你访问到一个 IP 地址(外部)时,他会被映射到内部多个预先保存的 IP 地址(内部)中的一个。

第七层的负载均衡,是根据HTTP请求头的内容,来指向不同的服务器。

硬件实现,通常是通过交换机。软件实现,就各有所长了。

例如 Nginx 就是典型的通过软件的第七层来实现负载均衡。

【4】混合形式

比如:

根据访问者的地区,找不同地区的机房(例如来自长三角的流量找上海的机房)根据访问者的网络,找不同运营商的机房(电信的找电信机房)进了机房后,根据机子的负载不同,找负载低的服务器

2、Nginx反向代理的基本配置

配置很简单,核心属性是proxy_pass,可以直接指向域名

解释:

当访问 时,触发这里的反向代理配置(当然,需要先配置好host,让这个域名指向nginx服务器的IP),然后会重定向到 proxy_pass 配置的IP地址。

即:【访问nginx服务器】–反向代理–>【103.94.185.215:3000】

server {listen 80;# 监听的域名是 ,即用户访问 时,触发这个配置server_name ;location / {# 访问根域名,跳转到 http://103.94.185.215:3000/ 这个IP,这个IP是我自己的网站proxy_pass http://103.94.185.215:3000/;}}

3、反向代理到多个服务器

以上只能反向代理到单个服务器,nginx也支持配置反向代理到多个服务器。

通过借用 upstream 属性,存储一个服务器列表,具体写法如下:

# 配置服务器列表upstream my_server_list{server 185.186.147.210;server 103.94.185.215:3000;}# 配置 serverserver {listen 80;# 监听的域名是 ,即用户访问 时,触发这个配置server_name ;location / {# 反向代理到 my_server_list 池中proxy_pass http://my_server_list;}}

注意:

upstream 和 server 的配置是同一级的;每一个 server 一行,只写ip即使用默认端口80,也可以写上端口;这里是最基本的配置,将在下面不断增加更复杂的内容;

4、属性详解

4.1 proxy_set_header

位置:

http, server, location

格式:

proxy_set_header field value;# 默认值proxy_set_header Host $proxy_host;proxy_set_header Connection close;

说明:

这个属性用于配置nginx服务器传给后端服务器的请求的 request 的 header 属性;具体来说,使用反向代理时,流量的流向形式是:【客户端】->【nginx服务器】->【后端服务器】;以host属性为例,默认情况下: 当访问 时,【客户端】->【nginx服务器】这一步,request 的 header 为 ;而【nginx服务器】->【后端服务器】这一步的header为proxy_pass属性中,http://属性后的字符串的值。例如proxy_pass http://127.0.0.1:3000;,则 host 的值为127.0.0.1:3000;如果是proxy_pass http://my_server_list;,那么传给后端服务器的header里 host 字段值为my_server_list; 如果想让 Host 值等于用户访问的域名,则需要这么设置proxy_set_header Host $host;也可以使用$http_host来替代$host,这个不同之处在于,当访问的不是 80/443 端口时,$http_host会带上端口值(示例::81) 几个属性值的区别,可以参考这个链接 关于nginx中host, server_name, http_host的区别 你也可以通过这个字段,来添加你想自定义的 header 属性对,或者覆写已有的字段值;注意:field 里的大写字母,在后端拿到的header里,大写字母都已经被转为小写字母了。我们之前在【07、日志】里面,通过http_x_forwarded_for来拿取重定向之前,用户真实的 IP 地址,因此我们通常在做反向代理的时候,应该通过这个属性,将用户真实 IP 地址添加进去。写法如下:proxy_set_header X-Forwarded-For $remote_addr;

如果自定义header的话,示例配置如下:

server {listen 80;# 监听的域名是 ,即用户访问 时,触发这个配置server_name ;location / {# 反向代理到 my_server_list 池中proxy_pass http://my_server_list;# 在发送到后端服务器的 request 里的 header 里添加属性头 Abc ,值为 abcdproxy_set_header Abc abcd;}}

后端服务器打个日志:

console.log('header-abc-', req.headers['abc']);

输出结果:

header-abc- abcd

4.2 upstream

upstream 在配置负载均衡时,提供了一个服务器池。每个服务器以server 关键字作为开头,配置一行。

upstream的配置和server配置平级。

server的配置很简单,都用下面这个:

server {listen 80;server_name ;location / {proxy_pass http://my_server_list/;}}

【默认配置】

轮询:在默认情况下,upstream按照轮询的方式实现负载,条件是时间;

upstream my_server_list{server 185.186.147.210;server 103.94.185.215:3000;}

【server】

语法:

server name [参数]

server:关键字,不变;name:域名、IP地址、端口号(不能只有端口号)、UNIX Socket;

【权重:weight】

写在 server 行中,默认值为1,值越大,负载权重越大。

当在 server 的服务器地址(例如IP)后面添加 weight 属性时,说明按权重的方式实现负载均衡。

weight 是通过数值的不同实现概率不同的,以下为例,理论上上面服务器被轮训到的概率是下面服务器的1/4;

upstream my_server_list{server 185.186.147.210 weight=1;server 103.94.185.215:3000 weight=4;}

【ip_hash】

每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。

简单来说,你第一次访问被分配到A服务器,那么后面永远都是A服务器来对你进行处理了。适合需要保存用户登录状态(session)的场景(但又做不到session共享的情况)。

这种情况缺点是不能保证每个服务器负载是相同的。

写法:

upstream my_server_list{ip_hash;server 185.186.147.210;server 103.94.185.215:3000;}

【fair:第三方插件】

需要安装第三方插件,效果是:按后端服务器的响应时间来分配请求,响应时间短的优先分配。

写法类似 ip_hash,安装方法谷歌搜索:【nginx安装fair】,下同。

【url_hash:第三方插件】

需要安装第三方插件,效果是:按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

写法类似 ip_hash

【down】

写在 server 行中,表示该服务器暂时不参加负载。

如以下示例中,你永远只能访问到第一个服务器。

upstream my_server_list{server 185.186.147.210 down;server 103.94.185.215:3000;}

之所以写 down,而不是直接注释掉。是因为 nginx 在做负载均衡时,是根据已有服务器做处理的。

例如4个server时的 ip_hash 和3个服务器的 ip_hash,在ip不变的情况下,命中的服务器是不一样的。

4个服务器一个后面写down,那还是四个服务器,而不是三个服务器。而注释掉,就真的只有3个服务器了。

【max_fails】

写在 server 行中,表示允许该服务器请求失败的次数,默认值为1,超过最大次数时,将被proxy_next_upstream这一行的配置所处理。

注意,这个过程不会被用户所感知,会被nginx所自动排除。

【fail_timeout】

写在 server 行中,表示在 max_fails 次失败后,该 server 暂停的时间(即在之后一段时间里,不会被nginx放在负载均衡池中),默认值是10s(即10秒)。

如果将这个值设置为fail_timeout=0,则表示如果遇见偶尔的错误情况,并不会将这个 server 排除在之后的请求(或者暂停一段时间不去请求这个服务器)。

示例写法如下(第一个服务器允许3次失败,失败后移出5秒)

upstream my_server_list{server 185.186.147.210 max_fails=3 fail_timeout=5s;server 103.94.185.215:3000;}

【backup】

写在 server 行中,表示在其他服务器都挂了(无一存活)的情况下,请求这个服务器。

默认情况下不会被请求,只有一台挂了的话,也不会被请求。

如下示例:

upstream my_server_list{server 185.186.147.210 max_fails=3 fail_timeout=5s;server 127.0.0.1:3000;server 103.94.185.215:3000 backup;}

若是前2个只有一个挂的情况下,也不会访问第三个服务器的。只有前2个都挂的情况下,才会访问。

适合在服务器都挂的情况下,给出一个静态的错误提示页面(静态页面是很难挂的)。

4.3、upstream 的相关变量

$upstream_addr:处理请求的 upstream 服务器地址;$upstream_status:upstream 服务器的应答状态;$upstream_response_time:upstream服务器的响应时间(秒),多个响应时间以逗号和冒号分割;$upstream_http_$HEADER:任意的HTTP协议头信息。注意,这个并不是指客户端请求的HTTP协议头里的信息。貌似是需要额外配置的。这些东西可以直接打到日志中;如果某个服务器错误(比如服务器挂了),重定向信息也会被写到同一行里;

示例:

1、日志格式

'[$request] | $upstream_addr | $upstream_status | $upstream_response_time s'

2、条件

服务器为:127.0.0.1:3000,访问链接/(本机host已经指向 nginx 服务器),忽略资源带来的日志(每个资源请求也会写入日志),日志结果为:

3、upstream只有一个服务器

[GET / HTTP/1.1] | 127.0.0.1:3000 | 200 | 0.010 s

4、upstream有两个服务器,但是一个服务器挂了(会触发nginx导向另外一个服务器)

[GET / HTTP/1.1] | 185.186.147.210:80, 127.0.0.1:3000 | 502, 200 | 0.287, 0.009 s

说明:

先访问到第一个服务器 185.186.147.210:80,但是因为服务器挂了,报502错误,访问时间是 0.287s;于是访问另外一个服务器 127.0.0.1:3000,服务器ok,状态码是200,访问时间是 0.009s;

5、以 URL 做区分实现负载均衡

常见场景:

有一个根域名,然后已开发了/a等多个服务已在线上正常运行。

现需要开发一个新业务,和之前的业务是独立的(即不需要和之前的页面交互),因为解耦原则,因此定该新业务的域名为/b

由于上新业务,当然最好是上新服务器(服务器组)咯。这样避免新业务down了影响老业务,也方便管理和开发。

因此,我们要实现以下效果:

访问/a,由x.x.x.1服务器(服务器组)处理;访问/b,由x.x.x.2服务器(服务器组)处理;x.x.x.2服务器在处理 url 的时候,不考虑/b。例如,当访问/a/abc的时候,服务器只匹配/abc,这样做的原因是好处很多,略;

实现方法:

1、先配置两组服务器的服务器池:

x.x.x.1服务器(服务器组,记为my_server_list_a),如下配置。(这里的IP地址是可用的,指向我自己的服务器,下同)

upstream my_server_list_a{server 185.186.147.210;}

x.x.x.2服务器(服务器组,记为my_server_list_b),如下配置。

upstream my_server_list_b{server 103.94.185.215:3000;}

2、根据 url 来配置 server:

server {listen 80;# 监听的域名是 ,即用户访问 时,触发这个配置server_name ;# 访问 /a 时触发这个逻辑location /a/ {# 反向代理到 my_server_list_a 池中,记得最后一定要加斜杠 /proxy_pass http://my_server_list_a/;# proxy_set_header X-Forwarded-For $remote_addr;}# 访问 /b 时触发这个逻辑location /b/ {# 反向代理到 my_server_list_b 池中proxy_pass http://my_server_list_b/;# proxy_set_header X-Forwarded-For $remote_addr;}}

3、done,搞定了。

4、唯一需要注意的是,前端页面在写路径时(例如css文件style.css位于服务器 b 的根路径下),不能写绝对路径/style.css,而是要写相对路径./style.css才可以。

5、类似的情况有:【各种资源文件】、【异步请求】等,都需要写相对路径。

6、解决方案:将静态资源和页面解耦,页面放在服务器下,而静态资源放在CDN下,在页面中写成绝对路径。当 URL 匹配时,后端服务器只返回符合要求的静态页面,静态页面自己去找路径符合的静态资源。(我在阿里时,就是这么实现的)

6、双机高可用

原因:

一个nginx服务器作为入口,如果nginx服务器挂了,后面服务将全部不可用。

解决办法:

一台服务器不够,那就上两台。但是两台nginx服务器怎么运作呢?

6.1、方法一:一主一备份

原理解释:

两个服务器,分别称为主服务器和备份服务器,两个在同一个网关下(必须);使用虚拟IP技术,两台服务器都指向这一个虚拟IP;初始情况下,主服务器生效(虚拟IP指向主服务器),流量全部走主服务器;当主服务器挂了(注意,是服务器挂了,而不是nginx服务挂了),备份服务器自动顶上去(即此时虚拟IP指向备份服务器);效果:主服务器即使挂了,备份服务器也能在几秒钟后接管;这种虚拟IP从主服务器指向备份服务器的过程,叫做【IP漂移】;

实现方法:

首先你要有一台主服务器,然后配置虚拟IP。

虚拟IP的实现方法,是让一台服务器指向多个IP,参考我之前写的那篇文章:05、一个nginx服务器为多个ip服务.md

假如主服务器的 IP 是192.168.1.11,备份服务器的 IP 是192.168.1.12

配置虚拟IP,让主服务器和备份服务器的虚拟 IP ,都指向192.168.1.100(先配主服务器的)。

注:制造备份服务器的简单解决办法,是先配置好主服务器,然后复制一份创建备份服务器(比如虚拟机,可以直接克隆一份)

此时访问192.168.1.100,会自动访问主服务器。

此时手动挂掉主服务器(比如直接关机),然后再访问192.168.1.100,有几秒无响应,过后会自动由备份服务器接管。

6.2、方法二:利用 Keepalived 双机同存

方法一中,入口只有一个虚拟IP,他只会指向其中一个nginx服务器,除非这个服务器挂了,或者手动漂移到另外一个服务器,否则虚拟IP不会指向另外一个服务器;而方法二,利用 Keepalived,可以自动切换虚拟IP指向的服务器,当服务器down了,或者nginx服务挂了,也会自动自动切换;

实现方法我给一个参考链接吧:/p/da26df4f7d60

7、更多nginx内容,请查看我的github

/qq20004604/nginx-demo

如果方便,麻烦给一个star,谢谢

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。