CnSwift - 雨燕

agile development - 敏捷开发

Nginx反向代理与负载均衡

Nginx反向代理与负载均衡

负载均衡和反向代理的概念

负载均衡

负载均衡是由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无需其它服务器辅助。通过某种负载均衡技术,将外部发送来的请求均匀分配到对称结构中的某一台服务器上,而接收到请求的服务器独立回应客户的请求。负载均衡能够平均分配客户请求到服务器阵列,做到快速获取数据,解决大量并发访问服务时响应慢问题。这种集群技术可以用最少的投资获得接近于大型主机的性能。

反向代理

反向代理(Reverse Proxy)是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外表现为一个服务器。

通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定代理服务器,并将本来要直接发送到Web服务器上的http请求发送到代理服务器中。由于外部网络上的主机并不会配置并使用这个代理服务器,普通代理服务器也被设计为在Internet上搜寻多个不确定的服务器,而不是针对Internet上多个客户机的请求访问某一个固定的服务器,因此普通的Web代理服务器不支持外部对内部网络的访问请求。

当一个代理服务器能够代理外部网络上的主机访问内部网络时,这种代理服务的方式称为反向代理服务。此时代理服务器对外就表现为一个Web服务器,外部网络就可以简单把它当做一个标准的Web服务器而不需要特定的配置。不同之处在于,这个服务器没有保存任何网页的真实数据,所有的静态网页或CGI程序,都保存在内部的Web服务器上。因此对反向代理服务器的攻击并不会使网页信息遭到破坏,这样就增强了Web服务器的安全性

反向代理方式和包过滤方式或普通代理方式并无冲突,因此可以在防火墙设备中同时使用这两种方式,其中反向代理用于外部网络访问内部网络时使用,正向代理或包过滤方式用于拒绝其它外部访问方式并提供内部网络对外部网络的访问能力。因此可以结合这些方式提供最佳的安全访问方式。

常见Web负载均衡方法

负载均衡的方法有很多,例如:用户自动选择方式(一些软件下载网站一般使用这种方式,提供多个下载链接,供用户自主选择)、DNS轮询方式、四/七层负载均衡设备等方法。

Nginx负载均衡与反向代理的配置实例

完整的Nginx反向代理示例
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
user  www www;

worker_processes auto;

error_log /home/wwwlogs/nginx_error.log crit;

pid /usr/local/nginx/logs/nginx.pid;

#Specifies the value for maximum file descriptors that can be opened by this process.
worker_rlimit_nofile 51200;

events
{
use epoll;
worker_connections 51200;
multi_accept on;
}

http
{
include mime.types;
default_type application/octet-stream;

server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;

sendfile on;
tcp_nopush on;

keepalive_timeout 60;

tcp_nodelay on;

fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 256k;

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";

#limit_conn_zone $binary_remote_addr zone=perip:10m;
##If enable limit_conn_zone,add "limit_conn perip 10;" to server section.

server_tokens off;
access_log off;

# 允许客户端请求的最大单个文件字节数
client_max_body_size 300m;

# 缓冲区代理缓冲用户端请求的最大字节数,可以理解为先保存到本地再传给用户
clinet_body_buffer_size 128k;

# 和后端服务器连接的超时时间 发起握手等候响应的超时时间
proxy_connect_timeout 600;

# 连接成功后,等候后端服务器响应时间,其实已经进入后端的排队之中等候处理
proxy_read_timeout 600;

# 后端服务器数据回传时间,就是在规定时间内后端服务器必须传完所有数据
proxy_send_timeout 600;

# 代理请求缓存区,这个缓存区间会保存用户的头信息以供Nginx进行规制处理,一般只要能保存下头信息即可
proxy_buffer_size 16k;

# 同上,告诉Nginx保存单个用户的几个buffer最大用多大空间
proxy_busy_buffers_size 64k;

# proxy缓存临时文件的大小
proxy_temp_file_write_size 64k;

upstream php_server_pool {
server 192.168.1.10:80 weight=4 max_fails=2 fail_timeout=30s;
server 192.168.1.11:80 weight=4 max_fails=2 fail_timeout=30s;
server 192.168.1.12:80 weight=2 max_fails=2 fail_timeout=30s;
}

upstream message_server_pool {
server 192.168.1.13:3245;
server 192.168.1.14:3245 down;
}

upstream bbs_server_pool {
server 192.168.1.15:80 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.1.16:80 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.1.17:80 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.1.18:80 weight=1 max_fails=2 fail_timeout=30s;
}
# 第一个虚拟主机,反向代理php_server_pool这组服务器
server
{
listen 80;
server_name www.yorudomain.com;
location /
{
# 如果后端的服务器返回502、504、执行超时等错误,自动将请求转到到upstream负载均衡池中的另一台服务器,实现故障转移。
proxy_next_upstream http_504 error timeout invalid_header;
proxy_pass http://php_server_pool;
proxy_set_header Host www.yourdomain.com;
proxy_set_header X-Forwarded-For $remote_addr;
}

access_log /data1/logs/www.yourdomain.com_access.log;
}
# 第二个虚拟主机
server {
listen 80;
server_name www1.yourdomain.com;

# 访问http://www1.yourdomain.com/message/***地址,反向代理message_server_pool这组服务区
location /message/
{
proxy_pass http://message_server_pool;
proxy_set_header Host $host;
}

# 访问除了/message/之外的http://www1.yourdomain.com/***地址,反向代理php_server_pool这组机器
location /
{
proxy_pass http://php_server_pool;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}

access_log /data1/logs/message.yourdomain.com_access.log;
}
# 第三个虚拟主机
server {
listen 80;
server_name bbs.yourdomain.com *.bbs.yourdomain.com;

location /
{
proxy_pass http://bbs_server_pool;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}

access_log off;
}
}

通过上述示例,我们已经看到Nginx对于多个域名的负载均衡是如何配置的。Upstream指令用于设置一组可以在proxy_passfastcgi_pass指令中使用的代理服务器,默认负载均衡的方式为轮询Upstream模块中的Server指令用于指定后端服务器的名称和参数,服务器的名称可以是一个域名、一个IP地址、端口号或UNIX Socket

而在server{...}虚拟机内,可以通过proxy_passfastcgi_pass指令设置进行反向代理的upstream服务器集群。

proxy_set_header指令用于在向反向代理的后端Web服务器发起请求时添加指定的Header头信息。

当后端Web服务器上有多个基于域名的虚拟主机时,要通过添加Header头信息的Host,用于指定请求的域名,这样后端Web服务器才能识别该反向代理访问请求由哪一个虚拟主机来处理。

使用反向代理之后,后端Web服务器(以PHP为例)就不能直接通过$_SERVER["REMOTE_ADDR"]变量来获取用户的真实IP了,通过$_SERVER["REMOTE_ADDR"]获取的将是Nginx负载均衡服务器的IP。

这时,就要通过在Nginx反向代理时添加Header头信息X-Forwarded-For,让后端Web服务器(以PHP为例)能够通过$_SERVER["HTTP_X_FORWARDED_FOR"]获取用户的真实IP。

Nginx负载均衡与反向代理实现动、静态网页分离

Nginx负载均衡的HTTP Upstream模块

ip_hash指令

ip_hash

语法:ip_hash

默认值:none

使用环境:upstream

当对后端的多台动态应用服务器做负载均衡时,ip_hash指令能够将某个客户端IP的请求通过哈希算法定位到同一台后端服务器上。这样,当来自某个IP的用户在后端Web服务器A上登录后,再访问该站点的其它URL,能够保证其访问的还是后端Web服务器A。如果不采用ip_hash指令,假设来自某个IP的用户在后端Web服务器A上登录后,再访问该站点的其它URL,有可能被定向到后端Web服务器B,C,…上,由于用户登录后SESSION信息是记录在服务器A上的,B,C,…上没有,这时就会提示用户未登录。

使用ip_hash指令无法保证后端服务器的负载均衡,可能有些后端服务器接收到的请求多,有些后端服务器接收到的请求少,而且设置后端服务器权重等方法将不起作用。所以,如果后端的动态应用服务器能够做到SESSION共享,还是建议采用后端服务器的SESSION共享方式来代替Nginx的ip_hash方式。

如果后端服务器有时要从Nginx负载均衡(已使用ip_hash)中摘除一段时间,你必须将其标记为“down”,而不是直接从配置文件中删掉或注释掉该后端服务器信息。代码示例如下:

1
2
3
4
5
6
7
upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com down;
server backend4.example.com;
}

这样,当原来为4台后端服务器时,摘除backend3.examole.com(标记为“down”)后,Nginx仍然会按4台服务器进行哈希。如果直接注释掉"server backend3.example.com;"这行,Nginx就会按照3台服务器进行重新哈希,原来被哈希到backend1.example.com的客户端IP有可能被哈希到backend2.example.com服务器上,原有的SESSION就会失效。

server指令

server

语法:server name [parameters]

默认值:none

使用环境:upstream

该指令用于指定后端服务器的名称和参数。服务器的名称可以是一个域名、一个IP地址、端口号或UNIX Socket。

在后端服务器名称之后,可以跟以下参数:

weight=NUMBER 设置服务器的权重,权重数值越高,被分配到的客户端请求数越多。如果没有设置权重,则为默认权重1.

max_fails=NUMBER 在参数fail_timeout指定的时间内对后端服务器请求失败的次数,如果检测到后端服务器无法连接及发生服务器错误(404错误除外),则标记为失败。如果没有设置,则为默认值1.设为数值0则关闭这项检查。

fail_timeout=TIME 在经理参数max_fails设置的失败次数后,暂停的时间。

down 标记为服务器为永久离线状态,用于ip_hash指令。

backup 仅仅在非backup服务器全部宕机或繁忙的时候才启用。

示例如下:

1
2
3
4
5
upstream backend {
server backend1.example.com weight=5;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
}
upstream指令

upstream

语法:upstream name {…}

默认值:none

使用环境:http

该指令用于设置一组可以在proxy_passfastcgi_pass指令中使用的代理服务器,默认的负载均衡方式为轮询。示例如下:

1
2
3
4
5
upstream backend {
server backend1.example.com weight=5;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
}
upstream相关变量

Nginx设置日志格式的log_format指令中,可以使用变量,例如:

1
2
3
4
5
6
log_format timing '$remote_addr - $remote_user [$time_local] $request '
'upstream_response_time $upstream_response_time '
'msec $msec request_time $request_time';

log_format up_head '$remote_addr - $remote_user [$time_local] $request '
'upstream_http_content_type $upstream_http_content_type';

$upstream_addr 处理请求的upstream服务器地址。

$upstream_status Upstream服务器的应答状态。

$upstream_response_time Upstream服务器响应时间(毫秒),多个响应以逗号和冒号分隔。

$upstream_http_$HEADER 任意HTTP协议头信息,例如:$upstream_http_host

Nginx负载均衡的双机高可用

更多精彩内容,请关注公众号 Golang语言开发栈公众号二维码
感谢赞赏.

Welcome to my other publishing channels