Apache的会话保持从发送协议上不同而有不同的配置,主要为http会话和ajp会话。
Apache与tomcat的结合非常好,默认情况下,采用最基本的配置即可做到ajp的会话保持,而http的会话保持就需要一些基础配置。
http的会话保持需要做到2点:
1.在单次通讯过程中,确保同一个client请求发送到相同的backend的ap;
2.当tcp连接断开,即tcp层面的连接超时后,会话session/cookie未超时的情况下,或已超时的情况下仍然将其发送至最初始的backend的ap;
因此常规思想是相同来源的ip发送至同一个backend的ap,这样会话保持的2点都可以做到,即简单原地址会话保持。这种做法最为简单,例如f5的会话保持策略,nginx的ip hash策略,都可以做到这种方式的负载均衡,但实际生产过程中还存在2个问题:
1.在实际的网络结构中,一个公网ip往往代表的是一个局域网的路由出口,所有的局域网内的client经过此出口出去后,在f5/nginx层面上会被视为同一来源,会将其转发至同一个backend的ap,造成backend的ap压力不均;
2.源地址会话保持是需要源地址信息的,某些网络结构或设备,如内网中的nginx、CDN网络,这种结构意味着负载均衡器无法接收到真正的client ip,大量的请求会转发到backend中的某一台ap上,造成负载不均;
额外说一下XFF hash的这种策略,XFF表面上解决了上述1,2两个问题,但实际上第一个问题并没有解决,因为你需要让client的出口路由主动添加XFF才能生效。
以上都是基于简单会话保持的讨论,除了简单会话保持之外常见的策略还有一种是基于sessionid的会话保持。
官方nginx版本没有这个功能,不过网上有第三方做的Nginx Sticky Module
From
,据说Tengine用的也是这个;另外也有nginx-upstream-jvm-route
From
。题外话 nginx pro是有这个功能的,不过要收费。F5做的sessionid的会话保持是存储sessionid与backend的ap映射关系,也就说有1W个请求,那么就有1W个映射关系,内存开销很大。
Apache提供的基于sessionid的会话保持的原理是,根据cookie的特定字段进行backend ap的路由匹配。例如apache官网给出的示例:
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<Proxy "balancer://mycluster">
BalancerMember "http://192.168.1.50:80" route=1
BalancerMember "http://192.168.1.51:80" route=2
ProxySet stickysession=ROUTEID
Proxy>
ProxyPass "/test" "balancer://mycluster"
ProxyPassReverse "/test" "balancer://mycluster"
From
示例中给出的为通过cookie中的ROUTEID字段进行匹配的方式,当ROUTEID字段匹配到1,那么请求就发送到1.50这个ap上。没有的话遵循最小连接发送请求。
配置这个关键字基本上就能解决几乎所有的负载均衡问题,但是,这里有一个天坑,如果查询资料的话,apache官网会告诉你stickysession这个字段支持JSESSIONID|jsessionid 然后这段配置改成如下:
ProxyPass "/test" "balancer://mycluster" stickysession=JSESSIONID|jsessionid scolonpathdelim=On
<Proxy "balancer://mycluster">
BalancerMember "http://192.168.1.50:80" route=node1
BalancerMember "http://192.168.1.51:80" route=node2
Proxy>
From
好了 按照此方法配置,会发现根本就不能会话保持!!!
这是因为apache的stickysession选择的字段需要有一个可识别后缀才可以识别出真正的转发凭据!!!
然后是最关键的地方,默认情况下当stickysession指定为JSESSIONID|jsessionid后,sticksession并不是完整的读取整个jsessionid,而且读取stickysessionsep所指定的符号之后的值,该开关默认情况下读取的是 “.",也就是说apache的jsessionid的会话保持实际上是读取jsessionid中的一部分信息,以此信息进行路由匹配来进行会话保持的。
例如Tomcat,需要在tomcat的server.xml的Engine配置中添加jvmRoute属性,eg:
jvmRoute的值要和apache转发配置文件中的route的值对应上,此时才完全做到了jsessionid的会话保持。
然后是Weblogic,WLS的jsessionid是由2部分构成的例如:
tAK1KJWjTMvsV6Ed3nEVXac5E8eh8NxtnGzNNTW7Rg-MGiLWdvzI!2084464100
以 “!”作为分隔符后面是primary_server_id,所以这里只需要知道只要后台的primary_server_id就可以负载均发,然而实际上不行,推荐使用mod_wl.so模块方式或者用添加routeid的方式进行负载均衡。
WAS的话好一些,使用“:”作为分隔符,但是比WLS好的地方在于重启之后WAS的id不会变,不过还是推荐用IBM自家的IHS和Plugin组合 。
有了这些就可以做到后台AP程序的真正的均匀分发请求,就可以用一个ap的压力程度来衡量整个系统的压力情况。同时可以在apache的日志模块中添加一些关键字,可以更加具体的知道请求去哪了。
日志关键字:
%{MYCOOKIE}C
The value contained in the cookie with name MYCOOKIE. The name should be the same given in the stickysession attribute.
%{Set-Cookie}o
This logs any cookie set by the back-end. You can track, whether the back-end sets the session cookie you expect, and to which value it is set.
%{BALANCER_SESSION_STICKY}e
The name of the cookie or request parameter used to lookup the routing information.
%{BALANCER_SESSION_ROUTE}e
The route information found in the request.
%{BALANCER_WORKER_ROUTE}e
The route of the worker chosen.
%{BALANCER_ROUTE_CHANGED}e
Set to 1 if the route in the request is different from the route of the worker, i.e. the request couldn't be handled sticky.
示例格式:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{JSESSIONID}C %{Set-Cookie}o %{BALANCER_SESSION_STICKY}e %{BALANCER_SESSION_ROUTE}e %{BALANCER_WORKER_ROUTE}e %{BALANCER_ROUTE_CHANGED}e"
日志样式:
192.168.32.1 - - [15/Oct/2019:16:42:28 +0800] "GET /test_session/ HTTP/1.1" 200 89 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" 3EED363F63C53F702B1604E7D152E8EF.jvm2 - JSESSIONID jvm2 jvm2 -
有时间测试nginx的那两个模块