nginx功能之反向代理及其负载均衡

简介

反向代理是客户端访问web服务器时,请求发送到真实的web服务器的前端”助手”服务器上,由”助手”服务器决定将此请求转发给哪个真实的web服务器,外界客户端以为”助手”服务器就是真实的web服务器,而实际上它不是,也不需要安装任何web程序。”助手”服务器称为反向代理服务器。

nginx是一个优秀的反向代理服务程序,通过反向代理可以实现负载均衡的效果。因为是通过反向代理实现的负载均衡,所以nginx实现的是七层负载均衡。它能够识别http协议,根据http报文将不同类型的请求转发到不同的后端web服务器上。后端的web服务器称为”上游服务器”,即upstream服务器。

实际上,nginx和php-fpm结合的时候,指令fastcgi_pass实现的也是反向代理的功能,只不过这种代理功能是特定的功能,只能转发给php-fpm。

nginx的反向代理有几种实现方式:

  1. 仅使用模块ngx_http_proxy_module实现简单的反向代理。指令为proxy_pass。
  2. 使用fastcgi模块提供的功能,反向代理动态内容。指令为fastcgi_pass。
  3. 使用ngx_http_memcached_module模块提供的功能,反向代理memcached缓存内容,指令为memcached_pass。
  4. 结合upstream模块实现更人性化的分组反向代理。

ngx_http_proxy_module模块

ngx_http_proxy_module模块是默认安装的,是用来显示反向代理的。主要有如下指令:

1
2
3
4
5
6
proxy_pass				定义代理到哪台服务器或哪个upstream池
proxy_set_header 在代理服务器上设置http报头信息。如加上真实客户端地址"proxy_set_header X_Forwarded_For $remote_addr"
proxy_connect_timeout 反向代理连接上游服务器节点的超时时间。发起方是proxy方,即等待握手成功的时间
proxy_send_timeout 上游服务器节点数据传给代理服务器的超时时间。即此时间段内,后端节点需要传完数据给代理服务器
proxy_read_timeout 定义代理服务器何时关闭和后端服务器连接的超时时长,默认为60秒,表示某次后端传输完数据给代理服务器后
如果60秒内代理服务器和后端服务器没有任何传输,则关闭此次连接。目的是避免代理服务器和后端服务器一直保持连接不断开而占用资源

模块参数介绍

proxy_pass

用法为 proxy_pass http[s]://{ [IP:PORT/uri/] | upstream_pool };后接后端服务器的IP地址或者是upsteamq名称。如下是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name halo.com;
client_max_body_size 1024m;

location / {
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8080/;
}
}

在nginx中配置proxy_pass代理转发时,如果在proxy_pass后面的url加/,表示绝对根路径;如果没有/,表示相对路径,把匹配的路径部分也给代理走。

假设下面四种情况分别用 http://192.168.1.1/proxy/test.html 进行访问。

第一种,代理到URL:http://127.0.0.1/test.html

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

第二种(相对于第一种,最后少一个 / ),代理到URL:http://127.0.0.1/proxy/test.html

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

第三种,代理到URL:http://127.0.0.1/aaa/test.html

1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1/aaa/;
}

第四种(相对于第三种,最后少一个 / ),代理到URL:http://127.0.0.1/aaatest.html

1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1/aaa;
}

在正常情况下,后端最好不要加URI。

proxy_set_header

允许重新定义或添加字段传递给代理服务器的请求头。该值可以包含文本、变量和它们的组合。

在没有定义proxy_set_header时会继承之前定义的值。默认情况下,只有两个字段被重定义:

1
`proxy_set_header Host $proxy_host;``proxy_set_header Connection close;`

一般情况下,设置如下头部信息即可:

1
2
3
4
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

upsteam模块

upstream 模块负债负载均衡模块,通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡。

1
2
3
4
5
6
7
upstream iyangyi {
ip_hash;
server 192.168.12.1:80;
server 192.168.12.2:80 down;
server 192.168.12.3:8080 max_fails=3 fail_timeout=20s;
server 192.168.12.4:8080;
}

在上面的例子中,通过upstream指令指定了一个负载均衡器的名称iyangyi。这个名称可以任意指定,在后面需要的地方直接调用即可。

里面是ip_hash这是其中的一种负载均衡调度算法,下面会着重介绍。紧接着就是各种服务器了。用server关键字表识,后面接ip。

Nginx的负载均衡模块目前支持4种调度算法:

  1. weight 轮询(默认)。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。weight。指定轮询权值,weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。
  2. ip_hash。每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。
  3. fair。比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。
  4. url_hash。按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身是不支持url_hash的,如果需要使用这种调度算法,必须安装Nginx 的hash软件包。

在HTTP Upstream模块中,可以通过server指令指定后端服务器的IP地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:

  • down,表示当前的server暂时不参与负载均衡。
  • backup,预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因此这台机器的压力最轻。
  • max_fails,允许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。
  • fail_timeout,在经历了max_fails次失败后,暂停服务的时间。max_fails可以和fail_timeout一起使用。

注意 当负载调度算法为ip_hash时,后端服务器在负载均衡调度中的状态不能是weight和backup。

缓存功能

nginx的ngx_http_proxy_module自带了缓存功能。跟反向代理的模块是一样的。当nginx做反向代理时,是可以进行缓存的,这样就可以减少跟后端服务器通信,从而提高性能。缓存后的数据在内存中有,也会放在设定的目录下。这样以后客户端继续请求相同资源时,可以直接从内存中或者自身的磁盘中获取并返回给客户端。当缓存超出指定的空间大小时,将会有一个专门的线程cache_manager来清理缓存。

定义的相关指令主要有3个:proxy_cache_path、proxy_cache、proxy_cache_valid。proxy_cache_path指令定义后只是定义了一种缓存方法,并非开启了缓存。而proxy_cache:定义要使用哪个缓存方法。使用proxy_cache_path中的name来引用。proxy_cache_valid根据状态码来指定缓存有效期。

proxy_cache_path语法比较复杂,但通常情况下是直接使用 proxy_cache_path path [levels=levels] keys_zone=name:size [max_size=size],具体含义如下:

  • path:定义缓存放在磁盘的哪个目录下。此处表示cache文件是保存在哪个目录下。目录不存在会自动创建。
  • levels:定义缓存目录的级别,同时定义缓存目录名称的字符数。例如levels=1:2:2表示3级目录,且第一级目录名1个字符,第二级目录2个字符,第三级目录2个字符。目录最多3级,目录名最多为2个字符。例如上例中”levels=1:2”产生的缓存文件路径可能是这样的”/usr/local/nginx/cache_dir/d/f1/50a3269acaa7774c02d4da0968124f1d”,注意其中加粗的字体。
  • keys_zone:定义缓存标识名称和内存中缓存的最大空间。name部分必须唯一,在后面会引用name来表示使用该缓存方法。
  • max_size:定义磁盘中缓存目录的最大空间。即path定义的文件最大空间。

proxy_cache_valid是可以根据状态码来指定缓存有效期。例如,下面的表示状态码为200和302的状态缓存1小时,状态码为404时即page not found的缓存只有1分钟,防止客户端请求一直错误,状态码为其他的则缓存5分钟。

1
2
3
proxy_cache_valid 200 302 1h;
proxy_cache_valid 404 1m;
proxy_cache_valid any 5m;

如果不指定状态码,只指定时间,则默认只缓存状态码200、301、302各5分钟,其他的状态码不缓存。

配置实例

nginx.conf加入proxy_cache_path配置。

/etc/nginx/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
http {
include /etc/nginx/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"';

access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

#gzip on;

proxy_cache_path /etc/nginx/cache_dir levels=1:2 keys_zone=cache_one:20m max_size=1g;
include /etc/nginx/conf.d/*.conf;
}

default.conf配置文件加入 proxy_cache proxy_cache_valid 规则:

/etc/nginx/conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 80;
server_name localhost;

location / {
proxy_pass http://192.168.1.61:8080;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache cache_one;
proxy_cache_valid 200 1h;
proxy_cache_valid 404 1m;
proxy_cache_valid any 5m;
}

add_header X-Cache "$upstream_cache_status from $server_addr";
}

测试如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost ~]# curl http://192.168.1.61/anatole/source/css/font-awesome.min.css |grep X-Cache
X-Cache: MISS from 192.168.1.61
[root@localhost ~]# curl http://192.168.1.61/anatole/source/css/font-awesome.min.css |grep X-Cache
X-Cache: HIT from 192.168.1.61
[root@localhost ~]# tree /etc/nginx/cache_dir/
/etc/nginx/cache_dir/
├── 3
│   └── 19
├── b
│   └── 09
└── c
└── 0e
└── 5cb0a17cc6641a451ed46888cecb90ec

fastcgi 配置

nginx与php相接合,一般是使用php-fpm,我们先来理解一下几个概念:

  1. cgi:它是一种协议。通过cgi协议,web server可以将动态请求和相关参数发送给专门处理动态内容的应用程序。
  2. fastcgi:也是一种协议,只不过是cgi的优化版。cgi的性能较烂,fastcgi则在其基础上进行了改进。
  3. php-cgi:fastcgi是一种协议,而php-cgi实现了这种协议。不过这种实现比较烂。它是单进程的,一个进程处理一个请求,处理结束后进程就销毁。一般是使用在windows的机器上面,性能比较低下。
  4. php-fpm:是对php-cgi的改进版,它直接管理多个php-cgi进程/线程。也就是说,php-fpm是php-cgi的进程管理器因此它也算是fastcgi协议的实现。在一定程度上讲,php-fpm与php的关系,和tomcat对java的关系是类似的。

fastcgi一般有以下参数:

1
2
3
4
5
6
7
8
9
10
11
12
fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2 keys_zone=TEST:10m inactive=5m;    
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 128k;
fastcgi_cache TEST;
fastcgi_cache_valid 200 302 1h;
fastcgi_cache_valid 301 1d;
fastcgi_cache_valid any 1m;

具体的解释如下:

  • 第一行代码是为FastCGI缓存指定一个文件路径、目录结构等级、关键字区域存储时间和非活动删除时间。
  • fastcgi_connect_timeout指定连接到后端FastCGI的超时时间。
  • fastcgi_send_timeout指定向FastCGI传送请求的超时时间,这个值是已经完成两次握手后向FastCGI传送请求的超时时间。
  • fastcgi_read_timeout指定接收FastCGI应答的超时时间,这个值是已经完成两次握手后接收FastCGI应答的超时时间。
  • fastcgi_buffer_size用于指定读取FastCGI应答第一部分需要用多大的缓冲区,这个值表示将使用1个64KB的缓冲区读取应答的第一部分(应答头),可以设置为fastcgi_buffers选项指定的缓冲区大小。
  • fastcgi_buffers指定本地需要用多少和多大的缓冲区来缓冲FastCGI的应答请求。如果一个PHP脚本所产生的页面大小为256KB,那么会为其分配4个64KB的缓冲区来缓存;如果页面大小大于256KB,那么大于256KB的部分会缓存到fastcgi_temp指定的路径中,但是这并不是好方法,因为内存中的数据处理速度要快于硬盘。一般这个值应该为站点中PHP脚本所产生的页面大小的中间值,如果站点大部分脚本所产生的页面大小为256KB,那么可以把这个值设置为“16 16k”、“4 64k”等。
  • fastcgi_busy_buffers_size的默认值是fastcgi_buffers的两倍。
  • fastcgi_temp_file_write_size表示在写入缓存文件时使用多大的数据块,默认值是fastcgi_buffers的两倍。
  • fastcgi_cache表示开启FastCGI缓存并为其指定一个名称。开启缓存非常有用,可以有效降低CPU的负载,并且防止502错误的发生,但是开启缓存也会引起很多问题,要视具体情况而定。
  • fastcgi_cache_valid、fastcgi用来指定应答代码的缓存时间,实例中的值表示将200和302应答缓存一个小时,将301应答缓存1天,其他应答均缓存1分钟。

内置预定义变量

内置预定义变量即无需声明就可以使用的变量,通常包括一个http请求或响应中一部分内容的值,以下为一些常用的内置预定义变量

变量名 定义
$arg_PARAMETER GET请求中变量名PARAMETER参数的值。
$args 这个变量等于GET请求中的参数。例如,foo=123&bar=blahblah;这个变量只可以被修改
$binary_remote_addr 二进制码形式的客户端地址。
$body_bytes_sent 传送页面的字节数
$content_length 请求头中的Content-length字段。
$content_type 请求头中的Content-Type字段。
$cookie_COOKIE cookie COOKIE的值。
$document_root 当前请求在root指令中指定的值。
$document_uri 与$uri相同。
$host 请求中的主机头(Host)字段,如果请求中的主机头不可用或者空,则为处理请求的server名称(处理请求的server的server_name指令的值)。值为小写,不包含端口。
$hostname 机器名使用 gethostname系统调用的值
$http_HEADER HTTP请求头中的内容,HEADER为HTTP请求中的内容转为小写,-变为_(破折号变为下划线),例如:$http_user_agent(Uaer-Agent的值);
$sent_http_HEADER HTTP响应头中的内容,HEADER为HTTP响应中的内容转为小写,-变为_(破折号变为下划线),例如: $sent_http_cache_control, $sent_http_content_type…;
$is_args 如果$args设置,值为”?”,否则为””。
$limit_rate 这个变量可以限制连接速率。
$nginx_version 当前运行的nginx版本号。
$query_string 与$args相同。
$remote_addr 客户端的IP地址。
$remote_port 客户端的端口。
$remote_user 已经经过Auth Basic Module验证的用户名。
$request_filename 当前连接请求的文件路径,由root或alias指令与URI请求生成。
$request_body 这个变量(0.7.58+)包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义。
$request_body_file 客户端请求主体信息的临时文件名。
$request_completion 如果请求成功,设为”OK”;如果请求未完成或者不是一系列请求中最后一部分则设为空。
$request_method 这个变量是客户端请求的动作,通常为GET或POST。包括0.8.20及之前的版本中,这个变量总为main request中的动作,如果当前请求是一个子请求,并不使用这个当前请求的动作。
$request_uri 这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI。
$scheme 所用的协议,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
$server_addr 服务器地址,在完成一次系统调用后可以确定这个值,如果要绕开系统调用,则必须在listen中指定地址并且使用bind参数。
$server_name 服务器名称。
$server_port 请求到达服务器的端口号。
$server_protocol 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$uri 请求中的当前URI(不带请求参数,参数位于args),不同于浏览器传递的 args),不同于浏览器传递的args),不同于浏览器传递的request_uri的值,它可以通过内部重定向,或者使用index指令进行修改。不包括协议和主机名,例如/foo/bar.html

参考资料

  • 本文作者: wumingx
  • 本文链接: https://www.wumingx.com/linux/nginx-proxy-upstream.html
  • 本文主题: nginx功能之反向代理及其负载均衡
  • 版权声明: 本博客所有文章除特别声明外,转载请注明出处!如有侵权,请联系我删除。
0%