記一次Spring Cloud Session微服務間傳遞丟失問題定位

在構建基於Spring Cloud微服務框架時,使用了常用的框架

NGINX+ZUUL+Eureka+業務服務,Session使用Spring boot的Redis集成,所有微服務間共享Session

所有業務的微服務Rest接口前臺調用接口通過ZUUL進行轉發,而ZUUL通過創建ZUULFilter過濾器來對請求鑑權及一些Session操作,而且為了保證Session實時生效,故設置Spring boot的Redis Session聲明為立即生效,如下所示:

@EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE)

在第一次業務請求時ZUUL過濾器會創建Session並設置屬性,然後直接將請求轉發到業務服務,


@Componentpublic class LoginFilter extends ZuulFilter { private final static Logger LOG = LoggerFactory.getLogger(LoginFilter.class);@Overridepublic Object run() throws ZuulException {Double rand = Math.random() * 100;int randInt = rand.intValue();RequestContext ctx = RequestContext.getCurrentContext();HttpServletResponse response = ctx.getResponse();HttpServletRequest request = ctx.getRequest();HttpSession session = request.getSession();session.setAttribute("test", randInt);LOG.info("Session id:{} test:{}",session.getId(),randInt);return null;} @Overridepublic boolean shouldFilter() {return true;} @Overridepublic int filterOrder() {return 0;} @Overridepublic String filterType() {return "pre";} }

然而業務服務在調用方法時通過request.getSession(false)獲取的Session為null,但緊接著第二次及後續調用時業務服務都能獲取到了正確的Session。


@RestControllerpublic class BusinessController {private final static Logger LOG = LoggerFactory.getLogger(BusinessController.class);@RequestMapping(path="/getsession/{key}")public String getSessionVal(HttpServletRequest request,@PathVariable("key") String key) {HttpSession session = request.getSession(false);Object value = null;if(null != session) {value = session.getAttribute(key);}LOG.info("Session id:{} value:{}",session == null ? null:session.getId(),value);return "";} }

這麼奇葩的現象,第一次Session創建成功居然Session不能傳遞,實在是令人匪夷所思。

由於沒有特殊定製過Spring boot Session機制,都是使用默認的Cookie傳遞方式。業務服務獲取不到Session,有兩種可能,一種是Redis沒有實時創建Session對象,另外一種是業務不能通過SESSIONID獲取到對象。

通過Debug斷點定位,在ZUUL創建Session時,通過Redis客戶端,直接連接到Redis服務器,查看發現Redis實時的生成了Session對象,故第一個假設應該不成立的。

然後通過打印在ZUUL與業務服務中Request Cookie信息,發現ZUUL在創建完Session後,並沒有更新request的Cookie中的SESSIONID,且request.isRequestedSessionIdValid()為false,故業務第一次獲取到的request中的Cookie沒有新的SESSIONID,就更不能根據Cookie獲取Session了,第二種假設成立。

通過查詢資料,明白了只有在請求Response響應後請求的Cookie才會更新,第一次請求時創建的Session不會在微服務間進行傳遞。天真的認為可以寬心的使用Spring cloud全家桶,殊不知還是需要細心與思考,同時也許需要知識積累。

從單體引用根本不會出現這種情況,Session在一個服務中傳遞,不存在Session重新通過Cookie獲取。而微服務簡化了業務,但多了交互,外部交流越多,越容易出錯,也許在微服務的路上還有更多的坑需要填補。

如何填補這個坑呢? 大家可以關注我的頭條號 以後會持續更新幹貨的