nginx sticky模块——记一次分发故障排查

背景介绍:

    Nginx:负载分发与反向代理,采用 Sticky 模式

    Server:后端服务  *  n

    浏览器登陆系统后,时而能正常加载登陆后的页面;时而白屏,一直在等待加载。(其他同事称之为闪退)

排查过程:


启动两个 server,浏览器登陆测试,并监控server日志。从server1上的日志可以看出,账号登陆正常了,从server2上的日志可以看到,无法正常请求到个人信息。由此暂时可以判断为:Nginx 的 sticky 模式未生效,登陆时请求分发到的 server1,但是登陆后的请求发送到了 server2。

server1 登陆日志
server2 个人信息加载日志



来看一下 Sticky 的原理:

    Sticky 是 nginx 的一个模块,它是基于 cookie 的一种 nginx 的负载均衡方案,其工作过程如下:

        1、客户端首次发起访问请求,nginx 接收后,发现请求头没有cookie,则以轮询方式将请求分发给后端服务器(注:cookie 是在 server 端生成的);

        2、后端服务器处理完请求,生成 cookie,并将 cookie 放入响应头中,将响应数据一同返回给 nginx ;

        3、Nginx 在 server 返回的 cookie 上,添加参数 route,生成新的cookie,返回给客户端(route 的值为一段hash 值,与后端服务器对应,可以认为是对后端 ip:port 的保护);

        4、客户端接收请求,并保存带 route 的 cookie ;

        5、.当客户端下一次发送请求时,会带上 route,nginx 根据接收到的 cookie 中的 route 值,转发给对应的后端服务器。



系统当前的现象明显和 Sticky 的原理相违背,接下来继续使用浏览器验证:

1、首次登陆,打开登陆页,一个 get 请求,response 里产生了 route 参数(route=67f8623da60b1cc87fa0b766b10fa558)。无论使用多少浏览器,只要后端是同一个server 在响应,都会是该值,这串值代表后端server 的 ip:port。

ehall域名的首次http请求

首页下载同域名下的 kb.js 文件,请求头中没有带上一次的 route,响应头中,Nginx又返回了一个 route 值(这次很巧,和上一步一致)。也就是说 route 在浏览器上丢了!

首页自动加载 kb.js 文件

首页下载同域名下的indexStdInfoContent.js 文件,请求头中依然没带上上一次的 route,响应头中,Nginx又返回了一个 route 值(这次和上一次不一致),这里 route 也在浏览器上丢了!

首页自动加载 indexStdInfoContent.js 文件  

2、在登陆框中输入用户名密码并提交,一个post请求,请求头里没有有 route 参数,响应头重新写入了 route 参数。这里明显有问题,route 并没有随着请求头传给nginx,nginx 又重新给了一个route,route 在浏览器上丢了!

登陆过程

3、继续观察,登陆成功后,加载个人信息的过程,请求头里有 route 值了,但 route 值和登陆过程中的 route 值不一致。也就是说用户在server1上登陆了,但是获取个人信息的请求发送到了第二个server2,server2 却没有他的登陆认证。

个人信息加载过程

上述显现让人很困惑,去和开发聊了一下,也没获得有用的信息,只能转回头再看一下Nginx

检查当前 Nginx 信息

openresty/1.7.7.1 + lua定制开发(应该是为了Service Mesh) + 加载了 nginx-sticky-module-1.1

如果是 Nginx 环境有问题呢?官网下载nginx及相关模块,搭建测试环境:

nginx/1.16.0  +  nginx-sticky-module-1.2.5

使用新环境验证:

1、首次登陆,打开登陆页,一个get 请求,response 里产生了 route参数(route=67f8623da60b1cc87fa0b766b10fa558)。

ehall域名的首次http请求

激动人心的地方,这里请求头里有了 route,而且和上一次请求一样。

首页自动加载 kb.js 文件
indexStudInfoContent.js 文件

2、在登陆框中输入用户名密码并提交,一个post请求,请求头里有了 route 参数!

登陆过程

3、加载个人信息的过程,请求头里有 route 值了,且和登陆过程中的 route 值一致。

加载个人信息

ok,经过2天的试验和总结,暂时告一段落。

可以通过更换 Nginx 的方式解决这一问题,但应该不是 Nginx 造成的。

因为:还有许多其它域名也通过当前的 Nginx 环境做反向代理和负载分发,经过观察,不存在这种现象。



开发同事的解决方案

通过控制上下文根来生成 route 值,方法如下:

首先访问uri:yjsxkapp/sys/index.do,然后重定向到原首页uri:yjsxkapp/sys/xsxkapp/*default/index.do 。

当访问 yjsxkapp/sys/index.do 时,上下文根是 yjsxkapp/sys,nginx 会基于该上下文根生成一个 route值;当重定向到 yjsxkapp/sys/xsxkapp/*default/index.do 时,上下文根是 yjsxkapp/sys/xsxkapp/*default ,相当于 yjsxkapp/sys 的子集,这时候 Nginx 便不会再重新生成新的 route值。

回顾一下直接访问 uri: yjsxkapp/sys/index.do 时的几个 request 请求,其 uri 分别如下:

yjsxkapp/sys/xsxkapp/*default/index.do

yjsxkapp/sys/xsxkapp/public/kb.js

yjsxkapp/sys/xsxkapp/public/indexBS.js

yjsxkapp/sys/xsxkapp/public/index.js

yjsxkapp/sys/xsxkapp/public/indexStdInfoContent.js

(如果后端server足够多,会发现他们5个请求,每个都会生成一个独立的 route 值,可以认为他们的请求是并发的,虽然时间略有延迟)



最终结论

产生上述情况的最终原因是 老版本的 sticky 模块,在同一个浏览器的并发请求中,并不会判断这些uri是否属于相同的上下文根子集;而高版本的 sticky 模块会进行判断并合并,后续请求会使用已有 route 值。

可以升级 sticky 版本;或人为让 sticky 模块先产生父上下文根的 route 值,再进行其它请求。

你可能感兴趣的:(nginx sticky模块——记一次分发故障排查)