1.3.5 基于cookie的会话保持处理机制
这里用HAProxy来举例说明。
任何一个L7的HTTP负载均衡器都应该具备一个功能:会话保持。会话保持是保证客户端对动态应用程序正确请求的基本要求。
还是用那个最有说服力的例子:客户端A向服务端B请求将C商品加入它的账户购物车,加入成功后,服务端B会在某个缓存区域中记录下客户端A和它的商品C,这个缓存的内容就是Session上下文环境。而识别客户端的方式一般是设置Session ID(如PHPSESSID、JSESSIONID),并将其作为cookie的内容交给客户端。客户端A再次请求的时候(比如为购物车中的商品下订单),只要携带这个cookie,服务端B就可以从中获取Session ID并找到属于客户端A的缓存内容(商品C),也就可以继续执行下订单部分的代码。
假如这时使用负载均衡软件对客户端的请求进行负载均衡,就必须要保证能将客户端A的请求再次引导到服务端B,而不能引导到服务端X、服务端Y,因为X、Y上并没有缓存与客户端A对应的Session内容,也就无法为客户端A下订单。
因此,反向代理软件必须具备将客户端和服务端“绑定”的功能,也就是所谓的提供会话保持,让客户端A后续的请求一定转发到服务端B上。
在LB上配置好HAProxy后,HAProxy将接受用户的所有请求。如果一个用户请求不包含任何cookie,那么这个请求将被HAProxy转发到一台可用的Web服务器上,可能是WebA、WebB、WebC或WebD。然后HAProxy将把处理这个请求的Web服务器的cookie值插入请求响应中,如SERVERID=A,若这个客户端再次访问并在HTTP请求头中带有SERVERID=A,HAProxy将会把它的请求直接转发给WebA处理。下面介绍实验的系统及开源软件的版本。
系统及开源软件版本:
·CentOS 7.6 x86_64
·HAProxy 1.7.9
·Nginx 1.12.2
·PHP 5.6.40
机器IP地址分配情况如下。
·HAProxy:192.168.100.22
·Nginx+PHP-1:192.168.100.23
·Nginx+PHP-2:1921.68.100.24
HAProxy代理两台Nginx机器,物理拓扑较简单,如图1-5所示。
图1-5 HAProxy代理Nginx物理拓扑图
首先,源码安装HAProxy 1.7.9,由于CentOS 7.6系统自带的HAProxy版本过低,这里想采用较高的开源版本,所以以源码方式进行安装:
cd /usr/local/src wget http://www.haproxy.org/download/1.7/src/haproxy-1.7.9.tar.gz tar xvf haproxy-1.7.9.tar.gz cd haproxy-1.7.9 make TARGET=linux2628 PREFIX=/usr/local/haproxy # TARGET指定编译OS对应的内核版本,这里写Linux2628即可 make install PREFIX=/usr/local/haproxy
为了实现基于cookie的会话保持,HAProxy配置文件中必须增加cookie的配置,如下所示:
# 需要转发的IP及端口 balance roundrobin cookie SERVERID insert indirect nocache server web1 192.168.100.23:80 cookie server1 server web2 192.168.100.24:80 cookie server2
在这个示例配置中,cookie指令中指定的是insert命令,表示在将响应报文交给客户端之前,先插入一个属性名为SERVERID的cookie,这个cookie在响应报文的头部独占一个Set-Cookie字段(因为是插入新cookie),而SERVERID只是cookie名称,它的值是由server指令中的cookie选项指定的,这里是server1或server2。
除了insert命令,cookie指令中还支持rewrite和prefix这两种设置cookie的方式,不过,对于这三种cookie的操作方式,只能三选一。
·insert:表示如果客户端没有cookie信息且有权限访问服务器时,持久性cookie必须通过HAProxy穿插在服务器的响应报文中。当服务器收到相同名称的cookie并且没有“preserve(保存)”选项时,将会移除之前已存的cookie信息。因此,insert可视作rewrite的升级版。cookie信息仅仅作为会话cookie且不会存到客户端的磁盘上。默认除非加了“indirect(间接)”选项,否则服务器端会看到客户端发送的cookie信息。由于缓存的影响,最好加上nocache或postonly选项。
·rewrite:表示cookie由服务器生成并且HAProxy会在其值中注入该服务器的标识符;此关键字不能在HTTP隧道模式下工作。
·prefix:表示不依赖专用的cookie做持久性,而是依赖现成的;用在某些特殊的场景中,如客户端不支持一个以上的cookie和应用程序对它有需求时。每当服务器建立一个名为<name>的cookie时,它将以服务器的标识符和分隔符作为前缀。来自客户端的请求报文中的前缀将会被删除以便服务器端能识别出它所发出的cookie,由于请求和响应报文都被修改过,所以此模式不能工作在隧道模式中,且不能与indirect共用,否则服务器端更新的cookie将不会被发到客户端。
这里参考一下HAProxy官方文档,它提供了cookie相关的配置说明,如下所示。
HAProxy将在客户端没有cookie时(比如第一次请求),在响应报文中插入一个cookie。
当没有使用关键词preserve选项时,如果后端服务器设置了一个与此处名称相同的cookie,则首先删除服务端设置的cookie。
该cookie只能作为会话保持使用,无法持久化到客户端的磁盘上(因为HAProxy设置的cookie没有maxAge属性,无法持久保存,只能保存在浏览器缓存中)。
默认情况下,除非使用了indirect选项,否则服务端可以看到客户端请求时的所有cookie信息。
由于缓存的影响,建议加上nocache或postonly选项。如果使用nocache选项,当客户端和HAProxy间存在缓存时,使用此选项和insert搭配最好,以便确保如果一个cookie需要被插入时,可被缓存的响应会被标记成不可缓存。这很重要,举个例子:如果所有的持久cookie被添加到一个可缓存的主页上,之后所有的客户将从外部高速缓存读取页面并将共享相同的持久性cookie,这会造成服务器阻塞。
最后,我们利用后端test.php文件来区分客户端连接的实际机器,test.php文件内容如下:
<h1>response from webapp 192.168.100.23</h1> <?php session_start(); echo "Server IP: "."<font color=red>".$_SERVER['SERVER_ADDR']."</font>"."<br>"; echo "Server Name: "."<font color=red>".$_SERVER['SERVER_NAME']."</font>"."<br data-tomark-pass>"; echo "SESSIONNAME: "."<font color=red>".session_name()."</font>"."<br data-tomark-pass>"; echo "SESSIONID: "."<font color=red>".session_id()."</font>"."<br data-tomark-pass>"; ?>
另一台机器相对应的内容改为:
<h1>response from webapp 192.168.100.24</h1>
接下来用如下地址访问HAProxy:
http://192.168.100.22/test.php
我们可以看一下访问http://192.168.100.22/test.php的结果显示,如图1-6所示。
图1-6 test.php访问结果图示
第一次访问时我们用Chrome浏览器的F12抓下HTTP的包,如图1-7所示。
图1-7 Chrome截图结果图示
再次访问时,对比一下,Response Headers已经没有Set_Cookie了,这是什么原因呢?
具体原因为:客户端在第一次收到响应后就会把cookie缓存下来,以后每次http://192.168.100.22/test.php(根据域名进行判断)都会从缓存中取出该cookie放进请求首部。这样HAProxy一定会将其分配给Web1,除非Web1下线了。
这样就实现了会话保持,保证被处理过的客户端能被分配到同一个后端应用服务器上。
参考文档: