前端反向代理nginx的负载均衡、读写分离及其缓存功能

nginx反向代理


实验前提:
1、nginx反向代理服务器系统平台为RHEL 6.4(64bit),后面2个httpd服务器系统平台为RHEL 5.8(32bit)。
2、nginx反向代理服务器地址为
  nginx反向代理服务器IP:192.168.108.230
  上游httpd服务器IP:192.168.108.199(apache1)   192.168.108.201(apache2)
3、确保3台服务器时间同步

 

实验结构拓扑:

wKioL1UUMt2jv5TWAABQ8f-QDaA175.jpg

 

 

 


一、nginx作为反向代理服务器
Nginx通过proxy模块实现反向代理功能。在作为web反向代理服务器时,nginx负责接收客户请求,并能够根据URI、客户端参数或其它的处理逻辑将用户请求调度至上游服务器上(upstream server)。nginx在实现反向代理功能时的最重要指令为proxy_pass,它能够将location定义的某URI代理至指定的上游服务器(组)上。如下面的示例中,location的/web将被替换为上游服务器上的/newweb。
不过这个/newweb必须在上游服务器(即我们常说的后端服务器)的根目录下创建。如果这个/newweb是个目录,还需要在这个目录下创建默认主页(如:index.html)。将nginx作为web代理服务器时,还需要在配置文件中添加如下信息:
 location /web {                                  
  proxy_pass http://www.xsl.com:8080/newweb; 

}   

这表示如果用户访问根目录下/web/下的默认主页时,实际访问的是上游服务器根目录下/newweb/下的默认主页。


添加完成之后,还需要在上游服务器中创建/newweb目录,
# mkdir  /www/xsl.com/newweb                    

这里的/www/xsl.com是我的上游httpd服务器的根目录,这里我是基于虚拟主机的方式来配置的。如果大家是采用的默认配置,则根目录为/var/www/html/,然后在这个目录下创建相应的目录和文件即可。
#vim   /www/xsl.com/newweb/index.html
<h1>node1.xsl.com</h1>

测试,在浏览器上输入http://192.168.108.230/web,测试结果如下:

wKiom1UUMGnwWCfcAADyP5CFf-w663.jpg


 
不过,这种处理机制中有两个例外。一个是如果location的URI是通过模式匹配定义的,其URI将直接被传递至上游服务器,因此不能为其指定转换的另一个URI。例如下面示例中的/bbs将被代理为http://www.xsl.com/bbs。
location ~ ^/bbs {
    proxy_pass http://www.xsl.com;   
}

注意:如果在location中基于正则表达式来匹配url的话,那么proxy_pass指令后面不能接指定的uri。如上例所示。



第二个例外是,如果在loation中使用的URI重定向,那么nginx将使用重定向后的URI处理请求,而不再考虑上游服务器上定义的URI。

如下面所示的例子中,传送给上游服务器的URI为http://host/index.php?page=<match>,而不是/index。因此,proxy_pass命令将不会被执行。

location / {
    rewrite /(.*)$ http://host/index.php?page=$1 ;
    proxy_pass http://localhost:8080/index;
}

 

如果location写成这种格式的话,那么uri被重写完成后,依然会传给上游服务器处理。

如下图例子中,uri被重写为/index.php?page=<match>后,该uri仍然会被传给上游服务器。因此,在这里真正请求的资源为http://localhost:8080/index/index.php?page=<match>。

location / {

    rewrite /(.$) /index.php?page=$1;

    proxy_pass http://localhost:8080/index;

}

 


二、upstream模块
与proxy模块结合使用的模块中,最常用的当属upstream模块。upstream模块可定义一个新的上下文,它包含了一组upstream服务器,这些服务器可能被赋予了不同的权重、不同的类型甚至可以基于维护等原因被标记为down。
upstream模块常用的指令有:
ip_hash:基于客户端IP地址完成请求的分发,它可以保证来自于同一个ip的请求始终被转发至同一个upstream服务器;
keepalive:每个worker进程为发送到upstream服务器的连接所缓存的个数;
least_conn:最少连接调度算法;
server:定义一个upstream服务器的地址,还可包括一系列可选参数,如:
    weight:权重;如果定义了多个server和weight,则用户的每一个请求会负载均衡的被上游服务器进行响应。这种负载均衡是基于加权轮调的方式进行的。
    max_fails:最大失败连接次数,失败连接的超时时长由fail_timeout指定;
     fail_timeout:等待请求的目标服务器发送响应的超时时长;
    backup:用于fallback的目的,所有服务均故障时才启动此服务器;
     down:手动标记其后端的上游服务器不再处理任何请求;

例如:在配置文件中添加如下将信息,注意upstream的配置要在http内server外
upstream webserver {
        server  192.168.108.199  weight=1  max_fails=3  fail_timeout=30s;
        server  192.168.108.201  weight=1;
}

location /up {
        proxy_pass  http://webserver;  这里接的是组名
}


添加完成之后,还需要在后端的2个上游服务器中创建up目录和默认主页。
192.168.108.199上的配置
# mkdir  /www/xsl.com/up
# vim  /www/xsl.com/up/index.html
<h1>up server1  node1.xsl.com</h1>
然后在重启httpd服务器
# service httpd  restart

192.168.108.201上的配置
# mkdir  /www/xsl.com/up
# vim  /www/xsl.com/up/index.html
<h1>up server2  node2.xsl.com</h1>

然后在重启httpd服务器
# service httpd  restart

测试,在浏览器上方输入http://192.168.108.230/up,第一次测试结果如下:

wKioL1UUMbLS6Jn2AAC-Z4SjKjU033.jpg


然后刷新该页面,显示结果如下:

wKiom1UUMInR4yAdAAC-dXiVXqA580.jpg


发现,用户的http请求均衡的被上游服务器进行响应。


当后端的所有服务器都down后,应该返回一个错误页面或提示页面给客户端,这样给用户看起来比较亲切。
需要在配置文件中添加如下信息:
server  127.0.0.1:8080  backup;   这一行信息添加在相应的upstream中。

server  {
        listen 8080;
        server_name  localhost;
        root /nginx/err ;
        index   index.html ;
      }

 
默认情况下,upstream使用的的复制均衡算法为轮调,有时候需要将同一个客户端的请求重定向到同一个服务器进行响应,以便重用session信息。为此,我们可以指定其算法为ip_hash,只要客户端的源ip相同,则以后该客户端所有的响应都由之前响应过的服务器进行响应。
ip_hash不可以和backup一起使用,它配置在upstream上下文中。

upstream模块的负载均衡算法主要有三种,轮调(round-robin)、ip哈希(ip_hash)和最少连接(least_conn)三种。

此外,upstream模块也能为非http类的应用实现负载均衡,如下面的示例定义了nginx为memcached服务实现负载均衡之目的。

upstream memcachesrvs {
    server 172.16.100.6:11211;
    server 172.16.100.7:11211;
}
 
server {
    location / {
    set $memcached_key "$uri?$args";
    memcached_pass memcachesrvs;
    error_page 404 = @fallback;
}
 

location @fallback {

    proxy_pass http://127.0.0.1:8080;
}
}


 
三、定义请求报文头部的源地址
默认情况下,当nginx作为前端代理服务器时,用户的请求是通过nginx反向代理服务器调度到后端的上游服务器。因此,当nginx反向代理器收到请求后,会重新对请求的报文进行封装,然后在封装后的报文交给后端的上游服务器。因此,当后端的上游服务器接收请求时,则认为请求的源地址为nginx服务器的地址。
对于需要统计用户访问的网站而言,如果请求报文的源地址为nginx服务器的地址的话是无法进行统计的。对于请求的源地址,我们可以通过查看后端httpd服务器的访问日志来显示。如:
192.168.108.230 - - [24/Mar/2015:23:03:47 +0800] "GET /up/ HTTP/1.0" 304 - "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)"
192.168.108.230 - - [24/Mar/2015:23:03:47 +0800] "GET /up/ HTTP/1.0" 304 - "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)"
192.168.108.230 - - [24/Mar/2015:23:03:47 +0800] "GET /up/ HTTP/1.0" 304 - "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)"
在这里192.168.108.230是nginx反向代理服务器的地址,因此,对于这种现象是无法统计用户的访问量的。


还好,在proxy模块中有一个命令proxy_set_header可以用来修改请求报文的源地址的。如果需要修改请求报文的真实源ip,可以在nginx反向代理服务器的配置文件中添加如下信息:
proxy_set_header X-Real-IP  $remote_addr;  这行命令添加在特定的location中即可。其中$remote_addr表示请求报文的源地址,后面X-Real-IP是一个自定义参数,这个参数的值就是$remote_addr,它会将该值传递给后端的上游服务器。

如:

location / {

        root   /www/htdoc;

        index  index.html index.htm;

        proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

}



在nginx代理服务器上添加完成之后,要想在upstream服务器上显示的是请求报文的真实ip地址,,还需要在httpd的主配置文件修改如下这行信息:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
将这行中的"%h"修改为%{X-Real-IP}i即可。如:
LogFormat "%{X-Real-IP}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
然后再在后端的服务器上查看httpd的相关日志信息,显示内容如下:
192.168.106.12 - - [26/Mar/2015:18:53:39 +0800] "GET /up/ HTTP/1.0" 200 34 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0; {D9D54F49-E51C-445e-92F2-1EE3C2313240}; 2345Explorer)"
192.168.106.12为客户端的ip地址,因此,此信息表明请求报文的源ip设置成功。


四、rewrite模块指令
nginx的重写模块是一个简单的正则表达式匹配与一个虚拟堆叠机结合。依赖于PCRE库,因此需要安装pcre。根据相关变量重定向和选择不同的配置,从一个location跳转到另一个location,不过这样的循环最多可以执行10次,超过后nginx将返回500错误。同时,重写模块包含set指令,来创建新的变量并设其值,这在有些情景下非常有用的,如记录条件标识、传递参数到其他location、记录做了什么等等。

rewrite模块包括如下几个指令:

rewrite

if

break

return

set


rewrite指令

rewrite可以实现uri重写,即将当前匹配的uri重写为另一个uri。rewrite指令根据配置文件中的顺序来执行。

1、rewrite语法格式:
语法:rewrite regex replacement flag。
rewrite可以用在server, location, if当中。通常rewrite用在location当中,并使用break标识符。也可以用在if上下文中。


2、重写规则
任何重写规则的第一部分都是一个正则表达式。可以使用括号来捕获,后续可以根据位置来将其引用,位置变量值取决于捕获正则表达式中的顺序,$1引用第一个括号中的值,$2引用第二个括号中的值,以此类推。
重写规则的第二部分是URI。URI可能包含正则表达式中的捕获的位置参数或这个级别下的nginx任何配置变量。注意:如果替换的字符串以http://开头,请求将被重定向,并且不再执行多余的rewrite指令。
第三部分也就是尾部的标记(flag)。


在rewrite语句后面还可以添加flag,这些flag可以是这些值:
last:本次重写完成之后,重新搜索location与更改后的URL匹配。这种flag是使用最多的,不过容易导致循环检查。不过循环次数最多也就10次。并返回状态码为500的错误。
break:本次重写完成后,直接执行后续操作。这种flag可以解决循环问题。
redirect:返回302临时重定向,如果替换字段是以http://开头则被重定向。
permanent:返回301永久重定向。
如:
location /image {
     index index.html;
     rewrite ^/image/bbs/(.*)$  http://192.168.108.201/jgp/$1;  

}

^/image/bbs/(.*)$ 表示uri以/image开头,如果uri不是"/"的话,那么^后面就必须加uri(如/image)。

  
  
if判断语句(最好用在location中)
在location中使用if语句可以实现条件判断,其通常有一个return语句,且一般与有着last或break标记的rewrite规则一同使用。但其也可以按需要使用在多种场景下,需要注意的是,不当的使用可能会导致不可预料的后果。
if语句中的判断条件
正则表达式匹配:
~    :与指定正则表达式模式匹配时返回“真”,判断匹配与否时区分字符大小写;

~*   :与指定正则表达式模式匹配时返回“真”,判断匹配与否时不区分字符大小写;

!~   :与指定正则表达式模式不匹配时返回“真”,判断匹配与否时区分字符大小写;
!~*  :与指定正则表达式模式不匹配时返回“真”,判断匹配与否时不区分字符大小写;


文件及目录匹配判断:
-f, !-f:判断指定的路径是否为存在且为文件;
-d, !-d:判断指定的路径是否为存在且为目录;
-e, !-e:判断指定的路径是否存在,文件或目录均可;
-x, !-x:判断指定路径的文件是否存在且可执行;
 
字符串比较
使用=或!=运算符


关键字:
break:终止退出。
return:直接返回状态码。可使用值包括:204,400,402-406,408,410,411,413,416以及500-504。也可以发送非标准的444代码-未发送任何头信息下结束连接。 

如: 
location / {
    if ($request_method == “PUT”) {
      proxy_pass http://upload.xsl.com:8080;
    }


    if ($request_uri ~ "\.(jpg|gif|jpeg|png)$") {
      proxy_pass http://imageservers;
      break;
     }
}

upstream imageservers {
 server 192.168.108.199:80 weight 2;
 server 192.168.108.201:80 weight 3;
}


set指令用来创建变量并进行赋值的。如:

set $a  "hello" :表示创建变量$a,且该值为"hello"。



五、nginx的防盗链功能

有时候我们常常看到某些uri请求首部中包含referer这样的首部,referer表示当前uri是从哪个页面跳转过来的。之所以会产生这种情况是因为当前的uri被此前的页面盗用了。uri被盗用可能会增加本地网络的流量,有些时候我们需要禁止uri资源被盗链。在nginx中,提供了一种防盗链的机制,这种机制通过valid_referers指令来完成的。

nginx的防盗链功能通常需要2个步骤来完成。

(1)、定义访问方式,即表示允许通过哪些方式来访问uri。需要借助valid_referers指令来完成。

valid_referer指令格式为:

valid_referer {none|block|server_names}。默认值为none。

            none:表示直接在浏览器输入网址来完成访问的。

            block:表示被防火墙标记过来的。

            server_names:表示这个域内的主机都可以访问,也可以指定一个特定的主机。

            

(2)、判断不合法的引用并对其处理。

通过借助if语句来完成判断并进行出来。



图片进行防盗链的事例如下:

在没有设置图片防盗链规则时,nginx的配置文件中添加如下内容:

location ~ /.*html$ {

            root /html/b;

}


location ~* \.(gif|png|gif)$ {

                root /tmp;

}


创建相应的目录及其文件

# mkdir -pv /html/b

# vim /html/b/2.html

<h1>hello,B</h1>

<img src="http://www.xsl.com/a.gif" />



访问http://www.xsl.com/2.html,显示的结果是:

wKioL1Z-qtmz5a2XAAByvZa2rkk573.png

没有设置图片防盗链规则时,图片可以正常浏览。

            

设置图片防盗链规则,添加如下内容:

valid_referers none block  127.0.0.1;

并在location ~* \.(gif|png|gif)$ {root /tmp;}中添加一个if语句,其该location最终的配置为:

location ~* \.(gif|png|gif)$ {

                root /tmp;

                if ($invalid_referer) {

                        return 403;

                }

}

$invalid_referer表示不合法的访问方式,即不属于valid_referers中定义的方式。


然后重载nginx

# service nginx reload


测试访问http://www.xsl.com/2.html,显示结果如下:

wKioL1Z-rV_wfa0kAABB-tqQ8NA762.png

该页面的图片没有正常显示出来。


抓包获取的状态码也是为403

wKiom1Z-rY-josgVAAAyYN3COdo144.png



 
六、nginx代理服务器实现读写分离
在这里我们使用的写操作为put方法,由于put方法是是基于WebDAV技术的。WebDAV (Web-based Distributed Authoring and Versioning) 一种基于 HTTP 1.1协议的通信协议。它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可直接对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的版本控制。
因此,需要在后端的httpd服务器中加载dav模块。如
LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so
加载完成之后,还需要在根目录<directory>容器中添加如下行:
dav  on

然后,编辑nginx的配置文件,修改文件内容为如下:
location / {
        proxy_pass  http://192.168.108.199/;
           if ($request_method = "PUT") {
                 proxy_pass http://192.168.108.201;
            }
}
然后还需要在192.18.108.201上设置apache用户对Apache的根目录具有写权限,如
#setfacl  -m u:apache:rwx  /www/xsl.com
 
然后再nginx服务器上上传文件
curl  -T /etc/issue  http://192.168.108.230

测试访问http://192.168.108.230/issue和访问http://192.168.108.199/issue即可发现读写分离以实现。这里不在贴图了。


 
 
七、nginx的cache功能
nginx做为反向代理时,能够将来自upstream的响应缓存至本地,并在后续客户端请求同样内容时直接从本地构造响应报文。
nginx的缓存空间包括共享内存空间和磁盘存储空间。其中共享内存空间存储的是键(key)和缓存对象的元数据。磁盘存储空间主要存储的就是数据。
如果想为nginx设定cahce功能,需要添加如下信息:

http {
    proxy_cache_path  /nginx/cache/web   levels=1:2  keys_zone=cachename:20m   miax_size=1G;    

注意proxy_cache_path: 不能定义在server{}上下文中;
   server { 

         location / {

            proxy_cache cachename;

            add_header  X-Via  $server_addr;          

            add_header X-Cache  $upstream_cache_status;   

            proxy_cache_vaild 200  1m; 
            proxy_cache_vaild any  10m;

            proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;

        }

   }

}



测试访问http://www.xsl.com是否命中。不过对于第一次访问时,其结果肯定是不命中的

# curl -I http://www.xsl.com

HTTP/1.1 200 OK

Server: nginx

Date: Thu, 17 Dec 2015 01:53:19 GMT

Content-Type: text/html; charset=UTF-8

Content-Length: 19

Connection: keep-alive

Last-Modified: Sat, 28 Nov 2015 22:10:53 GMT

ETag: "e06bc-13-525a114dea60d"

X-Via: 192.168.108.230

X-Cache: MISS

Accept-Ranges: bytes


不过第二次访问时,只要缓存是有效的,将直接从本地缓存中构建响应报文返回。

# curl -I http://www.xsl.com

HTTP/1.1 200 OK

Server: nginx

Date: Thu, 17 Dec 2015 01:56:07 GMT

Content-Type: text/html; charset=UTF-8

Content-Length: 19

Connection: keep-alive

Last-Modified: Sat, 28 Nov 2015 22:11:15 GMT

ETag: "e06bd-13-525a1162497fe"

X-Via: 192.168.108.230

X-Cache: HIT

Accept-Ranges: bytes



另外常用的三种缓存:
 open_log_cache:日志缓存
 open_file_cache:文件缓存
 fastcgi_cache: fastcgi缓存

 


 


你可能感兴趣的:(nginx,缓存,反向代理)