目录
1. 引言
在由nginx作为http负载均衡器加上若干台后端服务器的集群架构模型中,会话保持是一个经常会碰到的问题。当用户在不同的请求之间需要保持会话状态时,负载均衡器需要确保将用户的请求发送到最初处理该请求的服务器上,否则可能导致会话状态丢失引起不可预知的问题。 会话保持问题有多种解决办法,简单的就如本文提到的让nginx负载均衡器始终如一地将用户的请求路由到同一台后端服务器,这种方法不需要后端应用服务器考虑在集群间进行会话共享,简化了业务应用的开发过程。
nginx官方原生的系统是不支持负载均衡会话保持功能的,本文将介绍ngx_http_upstream_session_sticky_module模块来自于tengine,可以用来解决集群中应用会话保持的问题,本文将探讨如何使用该模块来实现会话保持,确保用户在整个会话期间都被发送到同一台服务器上。
2. 原理
该模块是利用浏览器的cookie功能来实现会话保持的,下面将详细介绍该模块通过Cookie实现会话保持的原理。
-
设置会话标识Cookie:
当客户端首次发送请求到Nginx负载均衡器时,ngx_http_upstream_session_sticky_module会在负载均衡器的响应中设置一个唯一的会话标识Cookie。该Cookie包含一个会话ID或其他唯一标识符,用于识别客户端的会话。 -
客户端发送带有会话标识Cookie的请求:
随后,客户端在后续的请求中会将会话标识Cookie作为请求头的一部分发送回负载均衡器。这样,负载均衡器就能够识别客户端的会话,并进行相应的处理。 -
nginx根据会话标识选择后端服务器:
当nginx收到带有会话标识Cookie的请求时,ngx_http_upstream_session_sticky_module会提取会话标识信息,并根据该信息选择一个后端服务器来处理请求。负载均衡器会检查会话标识与后端服务器的映射关系,并将请求转发到相应的服务器上。 -
会话保持配置指令:
ngx_http_upstream_session_sticky_module提供了一系列配置指令,允许管理员灵活地定义会话保持的行为。通过这些配置指令,管理员可以指定会话标识Cookie的名称、域、路径等选项。还可以定义会话标识的生成方法和存储方式,以及其他与会话保持相关的参数。 -
保持会话一致性:
通过使用Cookie进行会话保持,ngx_http_upstream_session_sticky_module确保客户端在整个会话期间都被发送到同一台服务器上。这样可以保持用户的登录状态、会话数据和其他与会话相关的信息的一致性。
3. 配置指令
3.1 session_sticky
语法:session_sticky [cookie=name] [domain=your_domain] [path=your_path] [maxage=time] [mode=insert|rewrite|prefix] [option=indirect] [maxidle=time] [maxlife=time] [fallback=on|off] [hash=plain|md5]
默认值:session_sticky cookie=route mode=insert fallback=on
上下文:upstream
说明:
本指令可以打开会话保持的功能,下面是具体的参数:
-
cookie
设置用来记录会话的cookie名称。 -
domain
设置cookie作用的域名,默认不设置。 -
path
设置cookie作用的URL路径,默认不设置。 -
maxage
设置cookie的生存期,默认不设置,即为session cookie,浏览器关闭即失效。 -
mode
设置cookie的模式:- insert: 在回复中本模块通过Set-Cookie头直接插入相应名称的cookie。
- prefix: 不会生成新的cookie,而是利用后端服务器和客户端之间交互的特定cookie,譬如jsp中的JSESSIONID或者asp.net中的ASP.NET_SessionId。当后端服务器响应的时候,nginx会在对应的cookie值前面加上特定的前缀,反过来当浏览器带着这个有特定标识的cookie再次请求时,模块在传给后端服务前先删除加入的前缀,后端服务拿到的还是原来的cookie值,这些动作对后端透明。如:“Cookie: NAME=SRV~VALUE”。
- rewrite: 使用服务端标识覆盖后端设置的用于session sticky的cookie。如果后端服务在响应头中没有设置该cookie,则认为该请求不需要进行session sticky,使用这种模式,后端服务可以控制哪些请求需要sesstion sticky,哪些请求不需要。
-
option
设置用于session sticky的cookie的选项,可设置成indirect或direct。indirect不会将session sticky的cookie传送给后端服务,该cookie对后端应用完全透明。direct则与indirect相反。 -
maxidle
设置session cookie的最长空闲的超时时间。 -
maxlife
设置session cookie的最长生存期。 -
fallback
设置是否重试其他机器,当sticky的后端机器挂了以后,是否需要尝试其他机器。 -
hash
设置cookie中server标识是用明文还是使用md5值,默认使用md5加密。
3.2 session_sticky_hide_cookie
语法: session_sticky_hide_cookie upstream=name;
默认值: none
上下文: server, location
说明:
配合proxy_pass指令使用,在向指定upstream后端服务器发起请求的时候需要,把nginx添加的cookie值删除掉,用在insert+indirect模式和prefix模式下。其中upstream表示需要进行操作的upstream名称。
4. 配置举例
下面举一个配置实例:
http{
upstream backend{
server 192.168.0.1;
server 192.168.0.2;
server 192.168.0.3;
session_sticky cookie=my_ession_sticky mode=insert option=indirect;
}
server {
listen 80;
session_sticky_hide_cookie upstream=backend;
}
}
5. 问题和思考
5.1 如果浏览器关闭了cookie,这个功能还能用吗?
本模块有一个应用的前提是,必须要浏览器能够支持cookie功能,虽然绝大多数情况下这是很容易满足的一个条件,但也有部分浏览器可能把cookie功能给关闭掉了,这样子就不能用本模块的功能了。
那这个时候怎么办?一个办法就是用ip hash负载均衡算法,这样子可以保证同样的来源ip只有upstream中的server保持不变就会hash到同一个上游服务器。
5.2 如果某台服务器故障了,那么之前路由到该服务器的请求将怎么处理?
如果对应的某台后端服务器挂了,我们可以想到,这个时候要么原来的那些会话要不就是报错,要么就是转给其他后端服务器提供服务。其实这个模块已经提供了这种配置选项。在session_sticky配置指令里面,有fallback=on|off的选项,设置是否重试其他机器,当sticky的后端机器挂了以后,是否需要尝试其他后端服务器,配置为off的话就直接响应502了。
5.3 对于部分url地址,如果不希望会话保持功能可以吗?
对于同一个域名下面的部分url地址,这些url地址和会话是没有关联的,譬如静态网页,或者图片、css、静态音视频内容等,如果不采用会话保持,它可以更好地通过负载均衡算法,将压力平摊到各个后端服务器。从目前版本的源码来看,本模块是不支持的,对比haproxy,我们发现是有这种能力的,希望ngx_http_upstream_session_sticky_module未来也能增加上此功能。
5.4 启用了本模块,某个浏览器的首次请求,负载均衡是采用什么算法?
当浏览器首次请求的时候,由于其没有nginx关心的cookie,那么nginx会采用什么算法进行负载均衡分配后端服务器呢?答案是swrr,即nginx内置的缺省负载均衡算法。
5.5 如果有多台nginx作为负载均衡器,是否仍然可以进行会话保持?
如果前端有多台nginx作为负载均衡器,当然nginx的前面可能是一个4层的网络负载均衡来负责nginx的负载均衡,那么同一个客户端浏览器的请求可能会在不同的nginx之间随机切换,那么是否仍然能够实现会话保持呢?
答案是肯定的,只要每台nginx配置的upstream是一样的,并且session_sticky的配置也是一样的,那么每台nginx仍然能够通过cookie解析到后端服务器的名字,从而找到正确的后端服务器,所以依然能够进行会话保持。
5.6 这个模块可以应用在官方原生nginx中吗?
众所周知,不少tengine中实现的增强模块是不能直接应用到官方原生的nginx中去的,譬如健康检查模块:ngx_http_upstream_check_module,因为这些模块使用的前提是需要对nginx核心源码进行修改,而本模块也依赖于tengine增加的server id的特性,涉及到对nginx核心代码的修改,是不能直接使用的。
不过可以简单修改一下本模块的代码去掉对server id特性的依赖就可以了,代码修改后,将本模块加到nginx中就可以编译通过,并且不影响本模块实现的功能。具体修改可以参考《NGINX代理架构如何保持后端应用服务器集群的会话[完整版]》
以上就是关于在ngx_http_upstream_session_sticky_module模块的使用和相关的思考。