简介
反向代理是客户端访问web服务器时,请求发送到真实的web服务器的前端”助手”服务器上,由”助手”服务器决定将此请求转发给哪个真实的web服务器,外界客户端以为”助手”服务器就是真实的web服务器,而实际上它不是,也不需要安装任何web程序。”助手”服务器称为反向代理服务器。
nginx是一个优秀的反向代理服务程序,通过反向代理可以实现负载均衡的效果。因为是通过反向代理实现的负载均衡,所以nginx实现的是七层负载均衡。它能够识别http协议,根据http报文将不同类型的请求转发到不同的后端web服务器上。后端的web服务器称为”上游服务器”,即upstream服务器。
实际上,nginx和php-fpm结合的时候,指令fastcgi_pass实现的也是反向代理的功能,只不过这种代理功能是特定的功能,只能转发给php-fpm。
nginx的反向代理有几种实现方式:
- 仅使用模块ngx_http_proxy_module实现简单的反向代理。指令为proxy_pass。
- 使用fastcgi模块提供的功能,反向代理动态内容。指令为fastcgi_pass。
- 使用ngx_http_memcached_module模块提供的功能,反向代理memcached缓存内容,指令为memcached_pass。
- 结合upstream模块实现更人性化的分组反向代理。
ngx_http_proxy_module模块
ngx_http_proxy_module模块是默认安装的,是用来显示反向代理的。主要有如下指令:
1 | proxy_pass 定义代理到哪台服务器或哪个upstream池 |
模块参数介绍
proxy_pass
用法为 proxy_pass http[s]://{ [IP:PORT/uri/] | upstream_pool };后接后端服务器的IP地址或者是upsteamq名称。如下是一个例子:
1 | server { |
在nginx中配置proxy_pass代理转发时,如果在proxy_pass后面的url加/,表示绝对根路径;如果没有/,表示相对路径,把匹配的路径部分也给代理走。
假设下面四种情况分别用 http://192.168.1.1/proxy/test.html 进行访问。
第一种,代理到URL:http://127.0.0.1/test.html
1 | location /proxy/ { |
第二种(相对于第一种,最后少一个 / ),代理到URL:http://127.0.0.1/proxy/test.html
1 | location /proxy/ { |
第三种,代理到URL:http://127.0.0.1/aaa/test.html
1 | location /proxy/ { |
第四种(相对于第三种,最后少一个 / ),代理到URL:http://127.0.0.1/aaatest.html
1 | location /proxy/ { |
在正常情况下,后端最好不要加URI。
proxy_set_header
允许重新定义或添加字段传递给代理服务器的请求头。该值可以包含文本、变量和它们的组合。
在没有定义proxy_set_header时会继承之前定义的值。默认情况下,只有两个字段被重定义:
1 | `proxy_set_header Host $proxy_host;``proxy_set_header Connection close;` |
一般情况下,设置如下头部信息即可:
1 | proxy_set_header HOST $host; |
upsteam模块
upstream 模块负债负载均衡模块,通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡。
1 | upstream iyangyi { |
在上面的例子中,通过upstream指令指定了一个负载均衡器的名称iyangyi。这个名称可以任意指定,在后面需要的地方直接调用即可。
里面是ip_hash这是其中的一种负载均衡调度算法,下面会着重介绍。紧接着就是各种服务器了。用server关键字表识,后面接ip。
Nginx的负载均衡模块目前支持4种调度算法:
weight 轮询(默认)。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。weight。指定轮询权值,weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。ip_hash。每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。fair。比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。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 | proxy_cache_valid 200 302 1h; |
如果不指定状态码,只指定时间,则默认只缓存状态码200、301、302各5分钟,其他的状态码不缓存。
配置实例
nginx.conf加入proxy_cache_path配置。
1 | http { |
default.conf配置文件加入 proxy_cache proxy_cache_valid 规则:
1 | server { |
测试如下:
1 | [root@localhost ~]# curl http://192.168.1.61/anatole/source/css/font-awesome.min.css |grep X-Cache |
fastcgi 配置
nginx与php相接合,一般是使用php-fpm,我们先来理解一下几个概念:
cgi:它是一种协议。通过cgi协议,web server可以将动态请求和相关参数发送给专门处理动态内容的应用程序。fastcgi:也是一种协议,只不过是cgi的优化版。cgi的性能较烂,fastcgi则在其基础上进行了改进。php-cgi:fastcgi是一种协议,而php-cgi实现了这种协议。不过这种实现比较烂。它是单进程的,一个进程处理一个请求,处理结束后进程就销毁。一般是使用在windows的机器上面,性能比较低下。php-fpm:是对php-cgi的改进版,它直接管理多个php-cgi进程/线程。也就是说,php-fpm是php-cgi的进程管理器因此它也算是fastcgi协议的实现。在一定程度上讲,php-fpm与php的关系,和tomcat对java的关系是类似的。
fastcgi一般有以下参数:
1 | fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2 keys_zone=TEST:10m inactive=5m; |
具体的解释如下:
- 第一行代码是为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 |