标题:nginx负载均衡+反向代理/rewrite,端口不一样造成网址无法访问的问题分析

发表于

问题描述:A机开启80端口,负载均衡所有网址到B机的8888端口和C机的8888端口,B机配置了一个反向代理,反向代理的应用还有一个网址转发,这样会造成网址无法访问。

前端服务器A 192.168.1.100 开启80端口,负载均衡
后端服务器B 192.168.1.101 开启8888端口,反向代理8080端口,8080端口还有网址rewrite
后端服务器C 192.168.1.102 开启8888端口

A机负载均衡配置如下:

upstream resinserver {
server 192.168.1.101:8888;
server 192.168.1.102:8888;
}

 

server {
listen 80;
location / {
proxy_pass http://resinserver/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

B机配置反向代理如下:

server {
listen 8888;
server_name paidc.com;
location / {
proxy_pass http://localhost:8080/;
}
}

开始分析问题,说明下,paidc.com是解析到A机上的:

正常来说HTTP请求是这样的:A paidc.com:80-->B paidc.com:8888-->B localhost:8080,如果localhost:8080程序执行正常,那么结果就没有什么问题了,网址http://paidc.com:80会被直接打开,也就是http://paidc.com

第一种情况:但是如果localhost:8080是个rewrite呢?请求执行到localhost:8080的时候,会被直接转向了,比如转到了index.php?id=5,由于这个请求是从B paidc.com:8888反向代理这里发出的,所以网址直接被转向到http://paidc.com:8888/index.php?id=5,然后浏览器再请求这个网址的时候,paidc.com:8888是被解析在A机IP 192.168.1.100上的,而A机又没有打开8888端口,所以,网址无法访问了

这种情况怎么解决呢?在B机的proxy配置上加上一条指定rewrite地址的语句,表示localhost:8080转发的时候,直接转发给paidc.com了,不要转发给paidc.com:8888,如下:

server {
listen 8888;
server_name paidc.com;
location / {
proxy_pass http://localhost:8080/;
proxy_redirect ~^http://localhost:8080(.*)   http://paidc.com$1;  //B机配置添加
}
}

第二种情况:问题深入考虑下,假如B机配置中如果有直接rewrite呢,根本就没有执行到proxy_pass http://localhost:8080/这一步,怎么办?A机paidc.com:80传给B机paidc.com:8888,然后paidc.com:8888直接转向到浏览器地址paidc.com:8888/index.php?id=5,这样还是解析到A机了,无法访问。

这种情况的解决办法是:在B机的nginx配置中把所有rewrite语句进行修改,要求转发到不带端口的网址上,强制用http://$/实现,例如:
把rewrite ^/(.*)? /$1 last;改成rewrite ^/(.*)? http://$host/$1 last;

并且在A机的配置上加上一条指定rewrite地址的语句,表示B机paidc.com:8888转发的时候,直接转发给paidc.com,不要转发给paidc.com:8888,如下:

server {
listen 80;
location / {
proxy_pass http://resinserver/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect ~^http://(.*):8888/(.*) http://$host/$2; //A机配置添加
}
}

上边两步确实可以解决如上网址无法访问的问题,只是相关的所有配置中都要添加语句,比较麻烦,还有可能漏掉。实行起来难度比较大!算是第一种方法吧。

实际上这种问题还有另外两种简单彻底的解决办法:

第二种方法是:在A机上添加一个端口转发,把所有paidc.com:8888的请求转发到paidc.com(缺点是:经过浏览器转发,会暴露B机8888端口,并且对于同一个网址,8888端口和80端口都是可以访问的,也不利于SEO),A机nginx配置如下:

server {
 listen 8088;
rewrite ^/(.*)$ http://$host/$1 last;
}
server {
listen 80;
location / {
proxy_pass http://resinserver/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

第三种方法是:保持ABC三台机器的端口一样,不要用A机的80端口负载均衡到B和C的8888端口上,都用80端口,就没有问题了。(缺点是知道B机打开了80端口,存在一定的安全隐患)

综上分析,其实最好的方法,还是不要折腾端口了,都用80端口,把B机和C机用防火墙做限制,80端口只对A机开放即可。