本篇内容是以别人的 博客文章演示所写出的,博客链接为:http://freeloda.blog.51cto.com/2033581/1288553
一、前言
在前面的几篇博文中我们主要讲解了Nginx作为Web服务器知识点,主要的知识点有nginx的理论详解、nginx作为web服务器的操作讲解、nginx作为LNMP架构的讲解,在这一篇博客中我们主要讲解, nginx的反向代理、负载均衡、缓存、URL重写演示。
二、实验环境
1、实验环境
2、关闭selinux、防火墙
[root@hpf-linux ~]# getenforce Disabled [root@hpf-linux ~]# iptables -F [root@node1 ~]# getenforce Disabled [root@node1 ~]# iptables -F [root@node2 ~]# getenforce Disabled [root@node2 ~]# iptables -F
3、同步时间
[root@hpf-linux ~]# ntpdate 202.120.2.101 [root@node1 ~]# ntpdate 202.120.2.101 [root@node2 ~]# ntpdate 202.120.2.101
三、Nginx之反向代理
在配置nginx反向代理之间我们得先准备两台测试服务器,node1和node2.
1、安装httpd
[root@node1 ~]# rpm -q httpd httpd-2.2.15-45.el6.centos.x86_64 [root@node2 ~]# rpm -q httpd httpd-2.2.15-45.el6.centos.x86_64
2、提供测试页
[root@node1 ~]# cat /var/www/html/index.htmlThis is node1 !
[root@node2 ~]# cat /var/www/html/index.htmlThis is node2 !
3、开启httpd
[root@node1 ~]# service httpd start [root@node2 ~]# service httpd start
4、测试节点的httpd是否正常启动
[root@hpf-linux ~]# curl http://192.168.1.9This is node1 !
[root@hpf-linux ~]# curl http://192.168.1.10This is node2 !
5、正向代理与反向代理基本概念
(1).正向代理的概念
正向代理,也就是传说中的代理,他的工作原理就像一个跳板,简单的说,我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器呢,他能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。
结论就是,正向代理 是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。
(2).反向代理的概念
继续举例:
例用户访问 http://www.test.com/readme,但www.test.com上并不存在readme页面,他是偷偷从另外一台服务器上取回来,然后作为自己的内容返回用户,但用户并不知情。这里所提到的 www.test.com 这个域名对应的服务器就设置了反向代理功能。
结论就是,反向代理正好相反,对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端,就像这些内容原本就是它自己的一样。
(3).两者区别
从用途上来讲:
正向代理的典型用途是为在防火墙内的局域网客户端提供访问Internet的途径。正向代理还可以使用缓冲特性减少网络使用率。反向代理的典型用途是将防火墙后面的服务器提供给Internet用户访问。反向代理还可以为后端的多台服务器提供负载平衡,或为后端较慢的服务器提供缓冲服务。另外,反向代理还可以启用高级URL策略和管理技术,从而使处于不同web服务器系统的web页面同时存在于同一个URL空间下。
从安全性来讲:
正向代理允许客户端通过它访问任意网站并且隐藏客户端自身,因此你必须采取安全措施以确保仅为经过授权的客户端提供服务。反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。
7、配置http反向代理
配置http反向代理:
[root@hpf-linux ~]# cp /etc/nginx/nginx.conf{,.bak} [root@hpf-linux ~]# vim /etc/nginx/nginx.conf location / { proxy_pass http://192.168.1.9; }
指令说明:proxy_pass
语法:proxy_pass URL
默认值:no
使用字段:location, location中的if字段
这个指令设置被代理服务器的地址和被映射的URI,地址可以使用主机名或IP加端口号的形式,
例如:proxy_pass http://localhost:8000/uri/;
8、测试
注,大家可以看到,当我们访问192.168.18.208时,被代理重新定向到Web1上。
9、查看一下Web服务器日志
[root@node1 ~]# tail /var/log/httpd/access_log 192.168.1.6 - - [10/Sep/2015:18:38:06 +0800] "GET / HTTP/1.0" 304 - "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.69 Safari/537.36 QQBrowser/9.1.3471.400"
可以看到这里的IP全是nginx代理服务器的IP,并不是真实客户端的IP。下面我们修改一下,让日志的IP显示真实的客户端的IP。
10、修改nginx配置文件
location / { proxy_pass http://192.168.1.9; proxy_set_header X-Real-IP $remote_addr; }
指令说明:proxy_set_header
语法:proxy_set_header header value
默认值: Host and Connection
使用字段:http, server, location
这个指令允许将发送到被代理服务器的请求头重新定义或者增加一些字段。这个值可以是一个文本,变量或者它们的组合。proxy_set_header在指定的字段中没有定义时会从它的上级字段继承。
加载配置文件:
[root@hpf-linux ~]# service nginx reload
测试并查看日志
[root@node1 ~]# tail /var/log/httpd/access_log 192.168.1.6 - - [10/Sep/2015:18:37:09 +0800] "GET / HTTP/1.0" 304 - "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.69 Safari/537.36 QQBrowser/9.1.3471.400"
大家可以看到日志记录的还是代理的IP,没有显示真实客户端的IP,我们来看一下httpd的配置文件。
11、查看并修改httpd配置文件
[root@node1 ~]# vim /etc/httpd/conf/httpd.conf
这是修改后的参数,将h%修改为%{X-Real-IP}i,好的下面我们再来测试一下。
从新载入配置文件
[root@node1 ~]# service httpd reload
查看node1的日志看是否显示:
[root@node1 ~]# tail /var/log/httpd/access_log 192.168.1.103 - - [10/Sep/2015:18:51:28 +0800] "GET / HTTP/1.0" 304 - "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.69 Safari/537.36 QQBrowser/9.1.3471.400"
大家可以看到现在的日志里记录的IP地址就是真实的客户端地址了。
四、Nginx之负载均衡
1、upstream 负载均衡模块说明
案例:
下面设定负载均衡的服务器列表。
upstream test.net{ ip_hash; server 192.168.10.13:80; server 192.168.10.14:80 down; server 192.168.10.15:8009 max_fails=3 fail_timeout=20s; server 192.168.10.16:8080; } server { location / { proxy_pass http://test.net; } }
upstream是Nginx的HTTP Upstream模块,这个模块通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡。在上面的设定中,通过upstream指令指定了一个负载均衡器的名称test.net。这个名称可以任意指定,在后面需要用到的地方直接调用即可。
2、upstream 支持的负载均衡算法
Nginx的负载均衡模块目前支持4种调度算法,下面进行分别介绍,其中后两项属于第三方调度算法。
轮询(默认)。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。Weight 指定轮询权值,Weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。
ip_hash。每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。
fair。这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。
url_hash。此方法按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身是不支持url_hash的,如果需要使用这种调度算法,必须安装Nginx 的hash软件包。
3、upstream 支持的状态参数
在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。
4、配置nginx负载均衡
[root@hpf-linux ~]# vim /etc/nginx/nginx.conf upstream webservers { server 192.168.1.9 weight=1; server 192.168.1.10 weight=1; } server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://webservers; proxy_set_header X-Real-IP $remote_addr; } }
upstream是定义在server{ }之外的,不能定义在server{ }内部。定义好upstream之后,用proxy_pass引用一下即可。
重新加载一下配置文件:
[root@hpf-linux ~]# service nginx reload
5、测试
6、配置nginx进行健康状态检查
max_fails,允许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。
fail_timeout,在经历了max_fails次失败后,暂停服务的时间。max_fails可以和fail_timeout一起使用,进行健康状态检查
upstream webservers { server 192.168.1.9 weight=1 max_fails=2 fail_timeout=2; server 192.168.1.10 weight=1 max_fails=2 fail_timeout=2; }
重新加载一下配置文件
[root@hpf-linux ~]# service nginx reload
node1停止服务器并测试:
[root@node1 ~]# service httpd stop
浏览器测试:
由于node1的服务停止,现在所以响应均会落到node2上。node1启动httpd服务后前端nginx还会将请求发往node1节点,但是一旦两个节点服务都停止了,前端就会返回错误页,下面我们可以定义一个好看的错误页面。
7、配置backup服务器
[root@hpf-linux ~]# vim /etc/nginx/nginx.conf upstream webservers { server 192.168.1.9 weight=1 max_fails=2 fail_timeout=2; server 192.168.1.10 weight=1 max_fails=2 fail_timeout=2; server 127.0.0.1:8080 backup; } server { listen 8080; server_name localhost; root /www/a.com; index index.html; } server { listen 80 ; server_name localhost; location / { proxy_pass http://webservers; proxy_set_header X-Real-IP $remote_addr; } }
提供测试页:
[root@hpf-linux ~]# cd /www/a.com/ [root@hpf-linux a.com]# cat index.htmlSorry..........
重新启动nginx服务:
[root@hpf-linux ~]# service nginx restart
关闭Web服务器并进行测试:
[root@node1 ~]# service httpd stop [root@node2 ~]# service httpd stop
8、配置ip_hash负载均衡
ip_hash,每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。
[root@hpf-linux ~]# vim /etc/nginx/nginx.conf upstream webservers { ip_hash; server 192.168.1.9 weight=1 max_fails=2 fail_timeout=2; server 192.168.1.10 weight=1 max_fails=2 fail_timeout=2; #server 127.0.0.1:8080 backup; }
重新加载一下服务器
[root@hpf-linux ~]# service nginx reload
测试一下:
测试之前请讲node1和node2的httpd服务启动。
大家可以看到,你不断的刷新页面一直会显示的是node1,说明ip_hash负载均衡配置成功。
五、Nginx之页面缓存
1、指令说明
proxy_cache_path :指令指定缓存的路径和一些其他参数,缓存的数据存储在文件中,并且使用代理url的哈希值作为关键字与文件名。levels参数指定缓存的子目录数。
语法:proxy_cache_path path [levels=number] keys_zone=zone_name:zone_size [inactive=time] [max_size=size];
默认值:None
使用字段:http
例如: proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m; 文件名类似于: /data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c
levels指定目录结构,可以使用任意的1位或2位数字作为目录结构,如 X, X:X,或X:X:X 例如: “2”, “2:2”, “1:1:2“,但是最多只能是三级目录。 所有活动的key和元数据存储在共享的内存池中,这个区域用keys_zone参数指定。one指的是共享池的名称,10m指的是共享池的大小。
注意每一个定义的内存池必须是不重复的路径,例如:
proxy_cache_path /data/nginx/cache/one levels=1 keys_zone=one:10m; proxy_cache_path /data/nginx/cache/two levels=2:2 keys_zone=two:100m; proxy_cache_path /data/nginx/cache/three levels=1:1:2 keys_zone=three:1000m;
如果在inactive参数指定的时间内缓存的数据没有被请求则被删除,默认inactive为10分钟。一个名为cache manager的进程控制磁盘的缓存大小,它被用来删除不活动的缓存和控制缓存大小,这些都在max_size参数中定义,当目前缓存的值超出max_size指定的值之后,超过其大小后最少使用数据(LRU替换算法)将被删除。内存池的大小按照缓存页面数的比例进行设置,一个页面(文件)的元数据大小按照操作系统来定,如FreeBSD/i386下为64字节,FreeBSD/amd64下为128字节。
proxy_cache :设置一个缓存区域的名称,一个相同的区域可以在不同的地方使用。
语法:proxy_cache zone_name;
默认值:None
使用字段:http, server, location
在0.7.48后,缓存遵循后端的”Expires”, “Cache-Control: no-cache”, “Cache-Control: max-age=XXX”头部字段,0.7.66版本以后,”Cache-Control:“private”和”no-store”头同样被遵循。nginx在缓存过程中不会处理”Vary”头,为了确保一些私有数据不被所有的用户看到,后端必须设置 “no-cache”或者”max-age=0”头,或者proxy_cache_key包含用户指定的数据如$cookie_xxx,使用cookie的值作为proxy_cache_key的一部分可以防止缓存私有数据,所以可以在不同的location中分别指定proxy_cache_key的值以便分开私有数据和公有数据。 缓存指令依赖代理缓冲区(buffers),如果proxy_buffers设置为off,缓存不会生效。
proxy_cache_valid:为不同的应答设置不同的缓存时间。
语法:proxy_cache_valid reply_code [reply_code …] time;
默认值:None
使用字段:http, server, location
例如:
proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m;
为应答代码为200和302的设置缓存时间为10分钟,404代码缓存1分钟。
2、定义一个简单nginx缓存服务器
缓存变量说明
$server_addr :服务器地址,在完成一次系统调用后可以确定这个值,如果要绕开系统调用,则必须在listen中指定地址并且使用bind参数。
$upstream_cache_status
MISS 未命中
EXPIRED - expired。请求被传送到后端。
UPDATING - expired。由于proxy/fastcgi_cache_use_stale正在更新,将使用旧的应答。
STALE - expired。由于proxy/fastcgi_cache_use_stale,后端将得到过期的应答。
HIT 命中
[root@hpf-linux ~]# vim /etc/nginx/nginx.conf proxy_cache_path /data/nginx/cache/webserver levels=1:2 keys_zone=webserver:20m max_size=1g; server { listen 80 ; server_name localhost; add_header X-Via $server_addr; add_header X-Cache $upstream_cache_status; location / { proxy_pass http://webservers; proxy_set_header X-Real-IP $remote_addr; proxy_cache webserver; proxy_cache_valid 200 10m; } }
新建缓存目录
[root@hpf-linux ~]# mkdir -pv /data/nginx/cache/webserver
重新加载一下配置文件
[root@hpf-linux ~]# service nginx reload
测试一下:
从图中我们可以看到,我们访问的服务器是192.168.1.6,缓存命中。
查看一下缓存目录:
[root@hpf-linux ~]# ls /data/nginx/cache/webserver/f/63/681ad4c77694b65d61c9985553a2763f /data/nginx/cache/webserver/f/63/681ad4c77694b65d61c9985553a2763f
六、Nginx之URL重写
1、URL重写模块(Rewrite)
这个模块允许使用正则表达式重写URI(需PCRE库),并且可以根据相关变量重定向和选择不同的配置。如果这个指令在server字段中指定,那么将在被请求的location确定之前执行,如果在指令执行后所选择的location中有其他的重写规则,那么它们也被执行。如果在location中执行这个指令产生了新的URI,那么location又一次确定了新的URI。这样的循环可以最多执行10次,超过以后nginx将返回500错误。
指令说明
break :完成当前设置的规则,停止执行其他的重写指令。
语法:break
默认值:none
使用字段:server, location, if
示例:
if ($slow) { limit_rate 10k; break; }
if :注意:在使用if指令之前请查看if is evil page并且尽量考虑用try_files代替。 判断一个条件,如果条件成立,则后面的大括号内的语句将执行,相关配置从上级继承。
语法:if (condition) { … }
默认值:none
使用字段:server, location
可以在判断语句中指定下列值:
一个变量的名称;不成立的值为:空字符传”“或者一些用“0”开始的字符串。
一个使用=或者!=运算符的比较语句。
使用符号~*和~模式匹配的正则表达式:
~为区分大小写的匹配。
~*不区分大小写的匹配(firefox匹配FireFox)。
!~和!~*意为“不匹配的”。
使用-f和!-f检查一个文件是否存在。
使用-d和!-d检查一个目录是否存在。
使用-e和!-e检查一个文件,目录或者软链接是否存在。
使用-x和!-x检查一个文件是否为可执行文件。
正则表达式的一部分可以用圆括号,方便之后按照顺序用$1-$9来引用。
return :这个指令结束执行配置语句并为客户端返回状态代码,可以使用下列的值:204,400,402-406,408,410, 411, 413, 416与500-504。此外,非标准代码444将关闭连接并且不发送任何的头部。
语法:return code
默认值:none
使用字段:server, location, if
rewrite :按照相关的正则表达式与字符串修改URI,指令按照在配置文件中出现的顺序执行。可以在重写指令后面添加标记。 如果替换的字符串以http://开头,请求将被重定向,并且不再执行多余的rewrite指令。
语法:rewrite regex replacement flag
默认值:none
使用字段:server, location, if
尾部的标记(flag)可以是以下的值:
last - 完成重写指令,之后搜索相应的URI或location。
break - 完成重写指令。
redirect - 返回302临时重定向,如果替换字段用http://开头则被使用。
permanent - 返回301永久重定向。
注意如果一个重定向是相对的(没有主机名部分),nginx将在重定向的过程中使用匹配server_name指令的“Host”头或者server_name指令指定的第一个名称,如果头不匹配或不存在,如果没有设置server_name,将使用本地主机名,如果你总是想让nginx使用“Host”头,可以在server_name使用“*”通配符(查看http核心模块中的server_name)。例如:
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last; rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra last; return 403;
但是如果我们将其放入一个名为/download/的location中,则需要将last标记改为break,否则nginx将执行10次循环并返回500错误。
location /download/ { rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break; rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra break; return 403; }
如果替换字段中包含参数,那么其余的请求参数将附加到后面,为了防止附加,可以在最后一个字符后面跟一个问号:
rewrite ^/users/(.*)$ /show?user=$1? last;
注意:大括号({和}),可以同时用在正则表达式和配置块中,为了防止冲突,正则表达式使用大括号需要用双引号(或者单引号)。例如要重写以下的URL:/photos/123456,为: /path/to/photos/12/1234/123456.png
则使用以下正则表达式(注意引号):rewrite "/photos/([0-9] {2})([0-9] {2})([0-9] {2})" /path/to/photos/$1/$1$2/$1$2$3.png;
如果指定一个“?”在重写的结尾,Nginx将丢弃请求中的参数,即变量$args,当使用$request_uri或$uri&$args时可以在rewrite结尾使用“?”以避免nginx处理两次参数串。 在rewrite中使用$request_uri将www.example.com重写到example.com:
server { server_name www.example.com; rewrite ^ http://example.com$request_uri? permanent; }
同样,重写只对路径进行操作,而不是参数,如果要重写一个带参数的URL,可以使用以下代替:
if ($args ^~ post=100){ rewrite ^ http://example.com/new-address.html? permanent; }
注意$args变量不会被编译,与location过程中的URI不同(参考http核心模块中的location)。
rewrite_log :启用时将在error log中记录notice 标记的重写日志。
语法:rewrite_log on | off
默认值:rewrite_log off
使用字段:server, location, if
变量:无
set :指令设置一个变量并为其赋值,其值可以是文本,变量和它们的组合。 你可以使用set定义一个新的变量,但是不能使用set设置$http_xxx头部变量的值。
语法:set variable value
默认值:none
使用字段:server, location, if
uninitialized_variable_warn :开启或关闭在未初始化变量中记录警告日志。 事实上,rewrite指令在配置文件加载时已经编译到内部代码中,在解释器产生请求时使用。
语法:uninitialized_variable_warn on|off
默认值:uninitialized_variable_warn on
使用字段:http, server, location, if
这个解释器是一个简单的堆栈虚拟机,如下列指令:
location /download/ { if ($forbidden) { return 403; } if ($slow) { limit_rate 10k; } rewrite ^/(download/.*)/media/(.*)\..*$ /$1/mp3/$2.mp3 break;
将被编译成以下顺序:
variable $forbidden checking to zero recovery 403 completion of entire code variable $slow checking to zero checkings of regular excodession copying "/" copying $1 copying "/mp3/" copying $2 copying ".mp3" completion of regular excodession completion of entire sequence
注意并没有关于limit_rate的代码,因为它没有提及ngx_http_rewrite_module模块,“if”块可以类似”location”指令在配置文件的相同部分同时存在。 如果$slow为真,对应的if块将生效,在这个配置中limit_rate的值为10k。
指令:
rewrite ^/(download/.*)/media/(.*)\..*$ /$1/mp3/$2.mp3 break;
如果我们将第一个斜杠括入圆括号,则可以减少执行顺序:
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break; 之后的顺序类似如下: checking regular excodession copying $1 copying "/mp3/" copying $2 copying ".mp3" completion of regular excodession completion of entire code