反向代理与负载均衡

  • 严格地说,Nginx仅仅是作为Nginx Proxy反向代理使用的,因为这个反向代理功能表现的效果是负载均衡集群的效果,所以本文称之为Nginx负载均衡。那么,反向代理和负载均衡有什么区别呢?
  • 普通负载均衡软件,例如大名鼎鼎的LVS,其实功能只是对请求数据包的转发(也可能会改写数据包),传递,其中DR模式明显的特征是从负载均衡下面的节点服务器来看,接收到的请求还是来自访问负载均衡器的客户端的真实用户,而反向代理就不一样了,反向代理接收访问用户的请求后,会代理用户重新发起请求代理下的节点服务器,最后把数据返回给客户端用户,在节点服务器看来,访问的节点服务器的客户端用户就是反向代理服务器了,而非真实的网站访问用户。

一句话,LVS等的负载均衡是转发用户请求的数据包,而Nginx反向代理是接收用户的请求然后重新发起请求去请求其后面的节点。
所以LVS是4层的负载均衡,而nginx则是7层的

实现Nginx负载均衡的组件主要有两个
image.png

该ngx_http_upstream_module模块用于定义可以由proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass和 grpc_pass指令引用的服务器组。

ngx_http_upstream_module模块允许Nginx定义一组或多组节点服务器组,使用时可以通过proxy_pass代理方式把网站的请求发送到事先定义好的对应Upstream组的名字上,具体写法为“proxy_pass http:// www_server_pools”,其中www_server_pools就是一个Upstream节点服务器组名字。
ngx_http_upstream_module模块官方地址为:
http://nginx.org/en/docs/http/ngx_http_upstream_module.html

测试环境准备

image.png
本次测试先使用一台负载均衡器,后续补充nginx负载均衡器的高可用

机器准备

客户端 192.168.0.86
负载均衡01 192.168.0.88
负载均衡02 192.168.0.89
web01 192.168.0.91
web02 192.168.0.92
vip 192.168.0.200

nginx的安装步骤这里略

nginx-proxy的负载均衡配置

查看nginx-proxy01的配置
[root@nginx-proxy01 nginx]# cat conf/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream web_server_pools {
server 192.168.0.91 weight=1;
server 192.168.0.92 weight=1;
}
server {
listen 80;
server_name www.yjs.com;
location / {
proxy_pass http://web_server_pools;
}
}
}
[root@nginx-proxy01 nginx]#

nginx-proxy的hosts配置

[root@nginx-proxy01 nginx]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.88 nginx-proxy01 www.yjs.com ops.yjs.com
192.168.0.89 nginx-proxy02
[root@nginx-proxy01 nginx]#

测试负载均衡访问

image.png

参数详解

https://blog.51cto.com/longlei/2138683

负载均衡的总体使用架构
upstream server_pools {
server 192.168.0.91:80(端口看情况是否省略) weight=1 max_conns=10 max_fails=5 fail_timeout=10 backup;
#还可以是域名类型upstream,例:
#server www.yjs.com:80(端口看情况是否省略) *;
#还可以是socket类型的upstream,例:
#server unix:/tmp/backend3; #指定socket文件
}
server {
location / {
proxy_pass http://server_pools;
health_check;
}
}

upstream只可以用在http模块下

upstream $定义一组服务器池 {
server $主机1 weight=权重 max_conns=最大连接数;默认0表示不限制 max_fails=某台Server允许请求失败的次数,超过最大次数后,在fail_timeout时间内,新的请求将不会分配给这台机器 fail_timeout=默认为10秒 backup备用/down宕机(机器状态特殊标识)
#server 192.168.0.91 weight=1 max_conns=10 max_fails=5 fail_timeout=10 backup;
}

定义好了upstream的服务器池,后端的引用配置

server {
listen 80;
server_name www.yjs.com;
location / {
proxy_pass http://$前面upstream定义的服务器池;
health_check;
}
}
这样就引用好了,就是这么简单

upstream模块调度算法

当负载调度算法为ip_hash时,后端服务器在负载均衡调度中的状态不能有weight和backup,即使有也不会生效。

1 rr 轮询

2 wrr 权重轮询 #以上两个算法不多说了

3 ip_hash ip哈希

https://blog.csdn.net/xqhys/article/details/81788358
根据ip来调度的,例:
nginx.conf的配置详情
[root@nginx-proxy01 nginx]# cat conf/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream web_server_pools {
ip_hash;
server 192.168.0.91;
server 192.168.0.92 down;
# server 192.168.0.91 weight=1;
# server 192.168.0.92 weight=1;
}
server {
listen 80;
server_name www.yjs.com;
location / {
proxy_pass http://web_server_pools;
}
}
image.png
ip_hash能访问到账指定的Ip机器上去

4 request_uri url哈希

nginx.conf的配置详情
[root@nginx-proxy01 nginx]# cat conf/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream web_server_pools {
hash $request_uri;
server 192.168.0.91;
server 192.168.0.92;
# server 192.168.0.91 weight=1;
# server 192.168.0.92 weight=1;
}
server {
listen 80;
server_name www.yjs.com;
location / {
proxy_pass http://web_server_pools;
}
}
}
image.png
url的哈希也是访问指定的机器上去

5 fair 动态调度

https://www.cnblogs.com/xiaohanlin/p/9904487.html
此算法会根据后端节点服务器的响应时间来分配请求,响应时间短的优先分配。这是更加智能的调度算法。此种算法可以根据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身不支持fair调度算法,如果需要使用这种调度算法,必须下载Nginx相关模块upstream_fair
upstream yunjisuan_lb{
fair;
server 192.168.0.91;
server 192.168.0.92;
}

6 least_conn 最少链接

https://www.cnblogs.com/minirice/p/10129726.html
least_conn算法会根据后端节点的连接数来决定分配情况,哪个机器连接数少就分发。
选取活跃连接数与权重weight的比值最小者为下一个处理请求的server。当然,上一次已选的server和已达到最大连接数的server照例不在选择的范围。
例如一个upstream有三台server:
upstream backend {
zone backends 64k;
least_conn;
server 10.10.10.2 weight=2;
server 10.10.10.4 weight=1;
server 10.10.10.6 weight=1;
}
假如上一个请求选择了第二台10.10.10.4,下一个请求到来,通过比较剩下可用的server的conns/weight值来决定选哪一台。
如果10.10.10.2连接数为100,10.10.10.6连接数为80,因为权重分别是2和1,因此计算结果
100/2=50, 80/1 =80。因为 50 < 80 所以选择第一台而不选第三台。尽管连接数第一台要大于第三台
可以发现91/92机器是来回切换的,均衡访问

7 least_time 最小的响应时间

计算节点平均响应时间,然后取响应最快的那个,分配更高权重。

反向代理多虚拟主机的节点

但是需要注意的是,也是前面章节遇到的一个问题,
两个域名地址的虚拟主机server,怎么能够访问到对应的server呢?
Nginx反向代理服务器,但是反向代理向下面节点重新发起请求时,默认并没有在请求头里告诉节点服务器要找哪台虚拟主机,所以,Web节点服务器接收到请求后发现没有主机头信息。
因此,就把节点服务器的第一个虚拟主机发给了反向代理了(节点上第一个虚拟主机放置的是故意这样放置的bbs)。
解决这个问题的方法,就是当反向代理向后重新发起请求时,要携带主机头信息,以明确告诉节点服务器要找哪个虚拟主机。
具体的配置很简单,就是在Nginx代理www服务虚拟主机配置里增加如下一行配置即可:
proxy_set_header host $host;
#在代理向后端服务器发送的http请求头中加入host字段信息,用于当后端服务器配置有多个虚拟主机时,可以识别代理的是哪个虚拟主机。这是节点服务器多虚拟主机时的关键配置。就算节点ops是在上面的,访问www的时候也不会出来ops的内容了

  1. server {<br /> listen 80;<br /> server_name www.yjs.com;<br /> location / {<br /> proxy_pass http://web_server_pools;<br /> }<br /> }

image.png

节点记录真实访问IP

代理服务器上的配置

server {
listen 80;
server_name www.yjs.com;
location / {
proxy_pass http://web_server_pools;
proxy_set_header X-Forwarded-For $remote_addr;
}
}

节点服务器上的配置

http {
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”‘;
后续的访问就可以查看到到真实IP了
image.png

规范化配置

server {
listen 80;
server_name www.yunjisuan.com;
location / {
proxy_pass http://www_server_pools;
include proxy.conf; #这就是包含的配置,具体配置内容见下文
}

cat proxy.conf
proxy_set_header host $host;
proxy_set_header x-forwarded-for $remote_addr;
proxy_connect_timeout 60;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;

根据目录地址request_uri转发—业务分离

当用户请求www.ysj.com/upload/xx地址时,实现由upload上传服务器池处理请求。
当用户请求www.yjs.com/static/xx地址时,实现由静态服务器池处理请求。
除此以外,对于其他访问请求,全都由默认的动态服务器池处理请求。
了解了需求后,就可以进行upstream模块服务器池的配置了。
先定义3个池,然后下面根据location或者if条件判断进行分配到哪个池


  1. #static_pools为静态服务器池,有一个服务器,地址为192.168.0.91,端口为80.<br /> upstream static_pools {<br /> server 192.168.0.91:80 weght=1;<br /> }<br /> #upload_pools为上传服务器池,有一个服务器地址为192.168.0.92,端口为80.<br /> upstream upload_pools {<br /> server 192.168.0.92:80 weight=1;<br /> }<br /> #default_pools为默认的服务器池,即动态服务器池,有一个服务器,地址为192.168.0.93,端口为80.<br /> upstream default_pools {<br /> server 192.168.0.93:80 weight=1;<br /> }

提示:需要增加一台测试Web节点Web03(ip:192.168.0.93),配置与Web01,Web02一样。
下面利用location或if语句把不同的URI(路径)请求,分给不同的服务器池处理,具体配置如下。


方案1:以location方案实现

  1. #将符合static的请求交给静态服务器池static_pools,配置如下:<br /> location /static/ {<br /> proxy_pass http://static_pools;<br /> include proxy.conf;<br /> }<br /> #将符合upload的请求交给上传服务器池upload_pools,配置如下:<br /> location /upload/ {<br /> proxy_pass http://upload_pools;<br /> include proxy.conf;<br /> }<br /> #不符合上述规则的请求,默认全部交给动态服务器池default_pools,配置如下:<br /> location / {<br /> proxy_pass http://default_pools;<br /> include proxy.conf;<br /> }

方案2:以if语句实现

  1. if ($request_uri ~* "^/static/(.*)$")<br /> {<br /> proxy_pass http://static_pools/$1;<br /> }<br /> if ($request_uri ~* "^/upload/(.*)$")<br /> {<br /> proxy_pass http://upload_pools/$1;<br /> }<br /> location / {<br /> proxy_pass http://default_pools;<br /> include proxy.conf;<br /> }

根据客户端设备user_agent转发—访问工具

这里还是使用static_pools,upload_pools作为本次实验的后端服务器池。下面先根据计算机客户端浏览器的不同设置对应的匹配规则。(由于没有合适的实验验证环境,这里仅作需求实现的细节讲解)

  1. location / {<br /> if ($http_user_agent ~* "MSIE")<br /> #如果请求的浏览器为微软IE浏览器(MSIE),则让请求由static_pools池处理<br /> {<br /> proxy_pass http://static_pools;<br /> }<br /> if ($http_user_agent ~* "Chrome")<br /> #如果请求的浏览器为谷歌浏览器(Chrome),则让请求由upload_pools池处理<br /> {<br /> proxy_pass http://upload_pools;<br /> }<br /> proxy_pass http://default_pools;<br /> #其他客户端,由default_pools处理<br /> include proxy.conf;<br /> }

除了针对浏览器外,上述“$http_user_agent”变量也可针对移动端,比如安卓,苹果,Ipad设备进行匹配,去请求指定的服务器,具体细节配置如下:
location / {
if ($http_user_agent ~ “android”)
{
proxy_pass http://android_pools; #这里是android服务器池
}
if ($http_user_agent ~
“iphone”)
{
proxy_pass http://iphone_pools; #这里是iphone服务器池
}
proxy_pass http://pc_pools; #这里是默认的pc服务器池
include extra/proxy.conf;
}


根据文件扩展名转发—文件类型

除了根据URI路径及user_agent转发外,还可以实现根据文件扩展名进行转发(这里仅以细节配置作为讲解内容,如需测试请同学们自行实验)
相关server配置
#先看看location方法的匹配规则,如下:

  1. location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {<br /> proxy_pass http://static_pools;<br /> include proxy.conf;<br /> }<br /> #下面是if语句方法的匹配规则:<br /> if ($request_uri ~* ".*\.(php|php5)$")<br /> {<br /> proxy_pass http://php_server_pools;<br /> }<br /> if ($request_uri ~* ".*\.(jsp|jsp*|do|do*)$")<br /> {<br /> proxy_pass http://java_server_pools;<br /> }

根据扩展名转发的应用场景
可根据扩展名实现资源的动静分离访问,如图片,视频等请求静态服务器池,PHP,JSP等请求动态服务器池。
location ~ ..(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
proxy_pass http://static_pools;
include proxy.conf;
}
location ~ .
.(php|php3|php5)$ {
proxy_pass http://dynamic_pools;
include proxy.conf
}
在开发无法通过程序实现动静分离的时候,运维可以根据资源实体进行动静分离,而不依赖于开发,具体实现策略是先把后端的服务器分成不同的组。
注意:
每组服务器的程序都是相同的,因为开发没有把程序拆开,分组后,在前端代理服务器上通过讲解过的路径,扩展名进行规则匹配,从而实现请求的动静分离。

综述

1 根据URL中的目录地址实现代理转发 例如动静分离那种
2 根据客户端的设备(user_agent)转发实践需求 例如浏览器的同
3 根据文件扩展名实现代理转发 例如动态文件还是静态文件

其他参数介绍

这里介绍一个参数 try_files
这个参数的作用就是当找不到首页文件的时候,就可以使用他了
格式
root html;
try_files $uri $uri/ /index.html;
意思就是
location / {
try_files $uri $uri/ /index.html;
}
当用户请求 http://localhost/example 时,这里的 $uri 就是 /example。
try_files 会到硬盘里尝试找这个文件。如果存在名为 /$root/example(其中 $root 是项目代码安装目录)的文件,就直接把这个文件的内容发送给用户。
显然,目录中没有叫 example 的文件。然后就看 $uri/,增加了一个 /,也就是看有没有名为 /$root/example/ 的目录。
又找不到,就会 fall back 到 try_files 的最后一个选项 /index.php,发起一个内部 “子请求”,也就是相当于 nginx 发起一个 HTTP 请求到 http://localhost/index.html。

例子 2
复制代码
loaction / {
try_files $uri @apache
}
loaction @apache{
proxy_pass http://127.0.0.1:88
include aproxy.conf
}
try_files方法让Ngxin尝试访问后面得$uri链接,并进根据@apache配置进行内部重定向。
当然try_files也可以以错误代码赋值,如try_files /index.php = 404 @apache,则表示当尝试访问得文件返回404时,根据@apache配置项进行重定向。