1 什么是cookie ?

参考 cookie
http://baike.baidu.com/view/835.htm
Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。定义于RFC2109(已废弃),最新取代的规范是RFC2965
Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,对于JSP而言也可以直接写入jsessionid,这样服务器可以知道该用户是否合法用户以及是否需要重新登录等
用途
服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续,这些都是Cookies的功用。另一个重要应用场合是“购物车”之类处理。用户可能会在一段时间内在同一家网站的不同页面中选择不同的商品,这些信息都会写入Cookies,以便在最后付款时提取信息。
生存周期
Cookie可以保持登录信息到用户下次与服务器的会话,换句话说,下次访问同一网站时,用户会发现不必输入用户名和密码就已经登录了(当然,不排除用户手工删除Cookie)。而还有一些Cookie在用户退出会话的时候就被删除了,这样可以有效保护个人隐私。
Cookie在生成时就会被指定一个Expire值,这就是Cookie的生存周期,在这个周期内Cookie有效,超出周期Cookie就会被清除。有些页面将Cookie的生存周期设置为“0”或负值,这样在关闭页面时,就马上清除Cookie,不会记录用户信息,更加安全。
识别功能
如果在一台计算机中安装多个浏览器,每个浏览器都会在各自独立的空间存放cookie。因为cookie中不但可以确认用户,还能包含计算机和浏览器的信息,所以一个用户用不同的浏览器登录或者用不同的计算机登录,都会得到不同的cookie信息,另一方面,对于在同一台计算机上使用同一浏览器的多用户群,cookie不会区分他们的身份,除非他们使用不同的用户名登录。
2 haproxy cookie 选项
参考 HAProxy 配置手册 4.2. session粘连相关
http://hi.baidu.com/maiyudaodao/item/a3cabab79c95847d254b09ea
Examples :
cookie JSESSIONID prefix
cookie SRV insert indirect nocache
cookie SRV insert postonly indirect
cookie SRV insert indirect nocache maxidle 30m maxlife 8h
See also : "appsession", "balance source", "capture cookie", "server" and "ignore-persist".
cookie [ rewrite | insert | prefix ] [ indirect ] [ nocache ]
[ postonly ] [ preserve ] [ domain ]*
[ maxidle ] [ maxlife ]
sections : defaults | frontend | listen | backend
yes | no | yes | yes

Arguments :
cookie的名称,用于持久性的监视、修改和增加。cookie在response中通过"Set-Cookie"头发送给客户端,并由客户端的所有request的"Cookie"头返回。尤其注意不要与应用cookie重名。另外,如果相同客户端使用了相同的后端(如HTTP/HTTPS),如果不希望跨后端的持久性,也要注意使用不同的cookie名称。
rewrite 表示cookie由服务器提供,haproxy必定要修改它的值,以包含服务器的标识符。这种模式便于应用来管理 "Set-cookie" 和 "Cache-control"头的复杂组合。应用可以决定是否适合发出一个持久性cookie。如果所有的响应都要被监控,则该模式只能工作在 HTTP close模式下。除非应用的行为很复杂和/或破碎(broken?),否则建议不要启动这个新的配置模式。关键字不能与"insert" 和 "prefix"共用。

insert 表示如果客户端还没有允许访问服务器的cookie,haproxy会在服务器响应中插入持久性cookie。
如果服务器发送了一个同名的cookie,当没有同时声明 "preserve" 选项时,它会在处理前被删掉。因此,这个模式可以用于升级“rewrite”模式下的已有配置。cookie只是一个会话cookie,而且不保存在客户端的磁盘中。 默认的,除非有选项 "indirect" ,服务器才会看到客户端发出的cookie。由于缓存的影响,通常明智的做法是添加关键字"nocache" or "postonly" 。关键字不能与 "rewrite" 和 "prefix"共用。
prefix 持久性基于现有的cookie就可以,而不必依赖专门的cookie。在一些特殊的环境下需要使用,如客户端不支持多于一个cookie,而应用已经需要了。这种情况下,每当服务器设置了cookie名字,haproxy就会在名字前添加一个服务器标识符和分隔符的前缀。前缀在所有客户端的请求中会被去掉,所以服务器还是会找到发送的cookie。既然所有的响应和请求都要被修改,这个模式则需要运行在HTTP close模式下。关键字不能与 "rewrite" 和 "insert"共用。
indirect 声明了该选项,没有cookie会被发送给客户端,即使客户端已经有一个合法的cookie给处理响应的服务器。如果服务器本身设置了这样一个cookie,也会被删除,除非同时设置了选项"preserve"。在"insert"模式下,还会删除发送给服务器的request的cookie。持久机制对应用是完全透明的。
nocache 如果在客户端和HAProxy之间有缓存,那么该选项建议与"insert"模式结合使用,如果需要新增cookie,确保可缓存的response标记为非缓存的。这很重要,因为所有可持久的cookie都被加在实例的一个可缓存的主页上,那么所有客户会从外部缓存获取页面,并都共享同样的持久cookie,导致一个服务器处理了太多的负载。参见"insert" 和 "postonly"选项。
postonly 该选项使cookie只能增加在POST请求的响应中。与 "nocache"选项只能二选一,因为POST响应是不可缓存的,所以持久cookie是绝不会被缓存的。第一个POST一般都是登录请求,大部分站点在此之前不需要任何持久性的排序,这可以避免在缓存中查找持久cookie,以有效优化缓存。参见"insert" 和 "nocache" 选项。
preserve 该选项可以与 "insert" 和/或 "indirect"一起使用。允许服务器自己发送持久cookie。在这种情况下,如果在response中发现有cookie,则HAProxy会对其保持不变。在实例的注销请求之后,需要用它来结束持久性。此时,服务器只需要发送一个带有无效值(如:empty)或过去日期的cookie。与"disable-on-404"检查选项相结合,可以实现一个完全正常的关闭,因为用户注销后一定会离开服务器。
domain 该选项用于指定cookie插入的域,后跟一个合法的域名参数。如果域名以点开头,浏览器允许任何以域名结尾的主机使用它。还可以通过多此调用此选项来指定多个域名,但要注意有的浏览器对域的个数是有限制的。有记录说在MSIE6或Firefox2上发送10个域是可以的。
maxidle 该选项允许在闲置时间后忽略插入的cookie,只对insert模式的cookie有效。当cookie发送到客户端,cookie的日期也被发送过去。进一步讲,如果日期早于参数(秒)表示的延迟,cookie会被忽略,否则会根据发送给客户端的响应进行刷新。尤其是用户一直不关闭浏览器,可以避免在一台服务器上占用太长时间 (eg: after a farm size change)。如果设置了选项,但是cookie没有日期,这种情况是允许的,只是会一直在响应中刷新。这维护了管理员访问他们站点的能力。cookie的日期如果超过未来24小时也会被忽略掉,使管理员可以在修复时区问题时不必把用户踢下线。
maxlife 该选项允许在生存时间过后忽略插入的cookie,不管是否还在使用,只对insert模式的cookie有效。当cookie发送到客户端,cookie的日期也被发送过去。进一步讲,如果日期早于参数(秒)表示的延迟,cookie会被忽略。如果request中的cookie没有日期,则会被设置一个日期。cookie的日期如果超过未来24小时也会被忽略掉,使管理员可以在修复时区问题时不必把用户踢下线。与maxidle不同,这个值是不刷新的,只在第一次访问日期计数。maxidle和maxlife可以一起使用,尤其是用户一直不关闭浏览器,可以避免在一台服务器上占用太长时间 (eg: after a farm size change)。与maxidle的超时后强行重新分配相比,这个要更好一些。
每个HTTP后端可以只有一个持久cookie,并定义在default部分,cookie的值可以是"server"声明中"cookie"关键字后面的值。如果服务器没有指定cookie,则不会设置。
3 例子 基于cookie插入的简单HTTP负载均衡
参考 haproxy架构指南1
http://blog.chinaunix.net/uid-10249062-id-222312.html

1. 基于cookie插入的简单HTTP负载均衡
192.168.1.1 192.168.1.11-192.168.1.14 192.168.1.2
-------+-----------+-----+-----+-----+--------+----
| | | | | _|_db
+--+--+ +-+-+ +-+-+ +-+-+ +-+-+ (___)
| LB1 | | A | | B | | C | | D | (___)
+-----+ +---+ +---+ +---+ +---+ (___)
haproxy 4 cheap web servers
配置 haproxy (LB1) :
-------------------------

 
    
  1. listen webfarm 192.168.1.1:80
  2. mode http
  3. balance roundrobin
  4. cookie SERVERID insert indirect
  5. option httpchk HEAD /index.html HTTP/1.0
  6. server webA 192.168.1.11:80 cookie A check
  7. server webB 192.168.1.12:80 cookie B check
  8. server webC 192.168.1.13:80 cookie C check
  9. server webD 192.168.1.14:80 cookie D check

描述:
-------------
- LB1 接收clients的requests
- 如果1个request不包含cookie,则把这个request前传到分配的一个有效的server
- 作为回报, 1个拥有server名称的cookie "SERVERID"会被插入到response中
- 当client带有cookie "SERVERID=A"再此访问时,LB1就会知道这个request必须被前传到server A. 同时删除这个cookie,server不会看到它
- 当server"webA"宕机时,request会被前传至另外一个有效的server,并且重新分配cookie
数据流:
注意:a 箭头方向;b HTTP 1.0;
-------
(client) (haproxy) (server A)
>-- GET /URI1 HTTP/1.0 ------------> |
( no cookie, haproxy forwards in load-balancing mode. )
| >-- GET /URI1 HTTP/1.0 ---------->
| <-- HTTP/1.0 200 OK -------------<
( the proxy now adds the server cookie in return )
<-- HTTP/1.0 200 OK ---------------< |
Set-Cookie: SERVERID=A |
>-- GET /URI2 HTTP/1.0 ------------> |
Cookie: SERVERID=A |
( the proxy sees the cookie. it forwards to server A and deletes it )
| >-- GET /URI2 HTTP/1.0 ---------->
| <-- HTTP/1.0 200 OK -------------<
( the proxy does not add the cookie in return because the client knows it )
<-- HTTP/1.0 200 OK ---------------< |
>-- GET /URI3 HTTP/1.0 ------------> |
Cookie: SERVERID=A |
( ... )
限制:
- 如果clients使用keep-alive (HTTP/1.1),只有第1个response会被插入1个cookie,并且在每个连接中只有第1个request会被解析。不过由于在第1个response中会立即插入cookie,并且在同一个连接中会把后续的request分配至同一个server所以在insertion模式下不会产生问题。然而,haproxy会删除前传到servers的cookie,所以server必须不能对未知的cookies敏感。如果这样会导致问题,可以通过添加下面的选项关闭keep-alive:
option httpclose
- 如果由于一些原因,clients无法学到多个cookie(例如:clients确实需要一些自制的应用程序或者网关),并且应用程序已经产生了一个cookie,可以通过是“prefix”模式。
- LB1成为1个非常敏感的server。如果LB1宕机,不再正常工作。可以通过keepalived进行热备。
- 如果应用程序需要记录原始的client IP地址,可以通过使用"forwardfor"选项增加包含原始client IP地址的"X-Forwarded-For" header。同样也需要使用"httpclose"保证重写每个request而不是仅仅每个连接的第1个request:
option httpclose
option forwardfor
- 如果应用程序需要记录原始的目的IP,可以通过使用"originalto"选项增加包含原始目的IP地址的"X-Original-To" header。同样也需要使用"httpclose"保证重写每个request而不是仅仅每个连接的第1个request:
option httpclose
option originalto
web serve必须配置为使用这些可替换的header。例如,apache,可以通过使用如下LogFormat:
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b " combined
CustomLog /var/log/httpd/access_log combined
提示:
-------
有时在网络中,可以发现有一定比例的client禁用了浏览器的cookies。显然,在网上有无处不在的问题,但是仍然可以通过使用"source"负载算法代替"roundrobin"从而帮助client访问你的网站。"source"负载算法可以在server数目不变的情况下保证一个给定的IP地址总会被分配至同1台server。因为会导致分配不均,所以不要在1个proxy或者1个小型网络后使用"source"负载算法。然而,在一个大型的内部网络和互联网,却可以工作的非常好。只要clients接受cookie,具有动态地址的Clients将不会受到影响。因为Cookie总是高于负载平衡的优先级

 
    
  1. listen webfarm 192.168.1.1:80
  2. mode http
  3. balance source
  4. cookie SERVERID insert indirect
  5. option httpchk HEAD /index.html HTTP/1.0
  6. server webA 192.168.1.11:80 cookie A check
  7. server webB 192.168.1.12:80 cookie B check
  8. server webC 192.168.1.13:80 cookie C check
  9. server webD 192.168.1.14:80 cookie D check