記一次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並設定屬性,然後直接將請求轉發到業務服務,
@Component public class LoginFilter extends ZuulFilter { private final static Logger LOG = LoggerFactory.getLogger(LoginFilter.class); @Override public 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; } @Override public boolean shouldFilter() { return true; } @Override public int filterOrder() { return 0; } @Override public String filterType() { return "pre"; } }
然而業務服務在呼叫方法時通過request.getSession(false)獲取的Session為null,但緊接著第二次及後續呼叫時業務服務都能獲取到了正確的Session。
@RestController public 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獲取。而微服務簡化了業務,但多了互動,外部交流越多,越容易出錯,也許在微服務的路上還有更多的坑需要填補。
如何填補這個坑呢?
下次再進行分析~~