一、背景
前端用nginx做横向扩展负载均衡,然后我这里用的是轮询策略,但是发现,在第一个节点登录后,访问第二个节点显示没有登录,此时是正常的,但是后面又访问了第一个节点,显示也没有登录,感觉每次轮询切换cookie中的id就变了一样,为什么呢?
二、过程分析
这里对同一个应用七两个节点,一个是8080,一个是8081,然后应用又两个接口,一个是登录,一个是下单,下单只有在登录的情况下才能下单,下面是浏览器访问过程分析。
1、第一次请求登录
在浏览器请求登录
http://127.0.0.1/login
Request Cookies为:925BF72A36EA953A7D908F19AF9BFEA5
Response Cookies为:C865EB29BEC515D7260AA6FB8F719692
因为第一次访问,请求的是8081那台机,后台session中没有JSESSIONID所以新生成一个,后续请求就以第二个JSESSIONID来请求。
2、第二次请求下单
在浏览器请求下单
http://127.0.0.1/order
Request Cookies为:C865EB29BEC515D7260AA6FB8F719692
Response Cookies为:B817F3B455CA8876A7C73E3042492514
因为nginx做了负载均衡,所以第二次亲求的是8080那台机,后台session中也没有JSESSIONID所以又新生成一个,后面就又用新生成的JSESSIONID来请求。
3、第三次请求下单
在浏览器请求下单
http://127.0.0.1/order
Request Cookies为:B817F3B455CA8876A7C73E3042492514
Response Cookies为:50B1182935BC7A0F1B773C86F7234669
因为nginx做了负载均衡,所以第三次请求的是8081那台机,但还是没有登录,因为你第二次新返回了JSESSIONID,跟第一次请求返回的不同,所以后台又生成了一个新的JSESSIONID,所以用户相当于没有登录了。
三、原因总结以及解决方案
1、原因总结
通过上面的测试可以知道,因为后台做了负载均衡,并且不是绑定IP的负载策略,用的是轮询的策略,所以每次轮询切换后,后端在内存中都找不到对应的JSESSIONID,判定为session失效而重新创建session,之前在session中保存的信息就会失效,所以当然登录失败啦,就算轮询到最初登录的那台机也没有用,中间已经生成了新的JSESSIONID给客户端。
2、解决办法
所以用轮询模式而不是用IP绑定的模式做负载均衡,需要将JSESSIONID做共享,比如放到数据库,或者redis,对于java来说用spring session是很方便的。