1. 程式人生 > >記一次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並設定屬性,然後直接將請求轉發到業務服務,

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

    如何填補這個坑呢?

   下次再進行分析~~