【乾貨分享】SpringCloud微服務架構分佈式組件如何共享session對象

一.簡單作一個背景說明
1.爲說明問題,本文簡單微服務架構示例以下web

clipboard.png

2.組件說明
分佈式架構,每一個組件都是集羣或者主備。具體說明以下:
zuul service:網關,API調用都走zuul service。
micro service1 & micro service2:業務功能實現,數據庫增刪改查。
eureka:組件註冊,zuul service,micro service等組件都註冊到eureka,管理組件調用地址。
db-master & db-slave:數據庫集羣,一主兩從。
redis master & redis slave:redis集羣,緩存。這裏主要存儲session對象。redis

3.組件之間API調用
①:網關zuul接收到的API請求,路由至業務實現組件。
②:網關zuul以及業務組件將session對象存儲到redis、或從redis獲取session對象。
③:業務組件實現數據增刪改查。
④:業務組件之間經過springCloud feign組件進行調用。
⑤:網關zuul以及micro service組件註冊到eureka組件,或從eureka獲取組件調用地址。spring

二.存在問題
基於如上微服務的分佈式架構若是按照傳統方式,將session對象存儲在內存中。在zuul網關將路由請求至不一樣的micro service1或者micro service2時,內存中的session對象將不能被共享,沒法判斷用戶的登錄狀態,也沒法獲取session對象存儲的全局數據。數據庫

三.解決方案
1.Spring管理session對象
經過EnableRedisHttpSession註解支持基於Redis存儲session,全局共享session對象。緩存

2.微服務架構下共享session對象實現說明
1)客戶端API請求到zuul,zuul基於spring管理session將session對象存儲到redis,並將生成的sessionId返回給客戶端。
2)zuul將請求路由到micro service,將sessionId經過cookie頭帶給micro service。
3)micro service經過sessionId從redis獲取到已經生成的session對象。
4)micro servcie1調用micro service2時,將sessionId也經過cookie頭帶給micro service2,micro service2經過sessionId從redis中獲取session對象。
5)客戶端再次調用時將a)步返回的sessionId增長到cookie頭,在redis中存儲的session失效以前zuul和micro service一直共享這個session。cookie

5.具體實現
1)經過springframework的EnableRedisHttpSession註解管理session,zuul和micro service組件實現這個類以存儲、獲取redis中存儲的session對象。session

import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = GlobalConstants.SESSION_TIMOUT, redisFlushMode = RedisFlushMode.IMMEDIATE)
public class SessionConfig {
    
}

EnableRedisHttpSession註解參數說明:
maxInactiveIntervalInSeconds:session過時時間配置。
redisFlushMode:redis session刷新模式。配置爲RedisFlushMode.IMMEDIATE,能夠確保zuul存儲到redis的session對象在請求到micro service中能當即被獲取。在實際開發過程當中出現因爲沒有這個配置值,有時候zuul將session對象存儲到了redis,可是micro service沒法當即獲取。架構

2)在zuul過濾器方法中調用addZuulRequestHeader增長請求頭,將sessionId經過cookie頭路由到micro service。app

public class AccessFilter extends ZuulFilter {

    @Autowired
    HttpServletRequest httpServletRequest;
    @Autowired
    HttpServletResponse httpServletResponse;

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        String sessionId = httpServletRequest.getSession().getId();
        ctx.addZuulRequestHeader("Cookie", "SESSION=" + sessionId);
        ctx.setSendZuulResponse(true);// 對該請求進行路由
        ctx.setResponseStatusCode(200); // 返回200正確響應

3)micro service1經過feign調用micro service2時,實現RequestInterceptor接口。經過增長cookie頭,將sessionId帶到micro service2。分佈式

@Configuration
public class MyRequestInterceptor implements RequestInterceptor {

    @Autowired
    HttpServletRequest request;

    @Override
    public void apply(RequestTemplate requestTemplate) {
        logger.info("MyRequestInterceptor apply begin.");
        try {
            String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
            if (null != sessionId) {
                requestTemplate.header("Cookie", "SESSION=" + sessionId);
            }
        } catch (Exception e) {
            logger.error("MyRequestInterceptor exception: ", e);
        }
    }
}

6.驗證
1)經過postman請求zuul服務地址,調用登錄接口。

clipboard.png

2)查看各組件sessionId
zuul sessionId:

clipboard.png

micro service1 sessionId:

clipboard.png

micro service1調用micro service2 sessionId:

clipboard.png

結論:能夠看到zuul和micro service中sessionId都是相同的,都是586b*c9a4,經過這種方式實現了API調用過程當中的session對象共享。

相關文章
相關標籤/搜索