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

記一次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並設置屬性,然後直接將請求轉發到業務服務,


  1. @Component
  2. public class LoginFilter extends ZuulFilter {
  3. private final static Logger LOG = LoggerFactory.getLogger(LoginFilter.class);
  4. @Override
  5. public Object run() throws ZuulException {
  6. Double rand = Math.random() * 100;
  7. int randInt = rand.intValue();
  8. RequestContext ctx = RequestContext.getCurrentContext();
  9. HttpServletResponse response = ctx.getResponse();
  10. HttpServletRequest request = ctx.getRequest();
  11. HttpSession session = request.getSession();
  12. session.setAttribute("test", randInt);
  13. LOG.info("Session id:{} test:{}",session.getId(),randInt);
  14. return null;
  15. }
  16. @Override
  17. public boolean shouldFilter() {
  18. return true;
  19. }
  20. @Override
  21. public int filterOrder() {
  22. return 0;
  23. }
  24. @Override
  25. public String filterType() {
  26. return "pre";
  27. }
  28. }

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


  1. @RestController
  2. public class BusinessController {
  3. private final static Logger LOG = LoggerFactory.getLogger(BusinessController.class);
  4. @RequestMapping(path="/getsession/{key}")
  5. public String getSessionVal(HttpServletRequest request,@PathVariable("key") String key) {
  6. HttpSession session = request.getSession(false);
  7. Object value = null;
  8. if(null != session) {
  9. value = session.getAttribute(key);
  10. }
  11. LOG.info("Session id:{} value:{}",session == null ? null:session.getId(),value);
  12. return "";
  13. }
  14. }

這麼奇葩的現象,第一次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獲取。而微服務簡化了業務,但多了交互,外部交流越多,越容易出錯,也許在微服務的路上還有更多的坑需要填補。

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


分享到:


相關文章: