1. 程式人生 > >SpringCloud微服務架構分散式元件如何共享session物件

SpringCloud微服務架構分散式元件如何共享session物件

一.簡單做一個背景說明
1.為說明問題,本文簡單微服務架構示例如下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物件。

3.元件之間API呼叫
①:閘道器zuul接收到的API請求,路由至業務實現元件。
②:閘道器zuul以及業務元件將session物件儲存到redis、或從redis獲取session物件。
③:業務元件實現資料增刪改查。
④:業務元件之間通過springCloud feign元件進行呼叫。
⑤:閘道器zuul以及micro service元件註冊到eureka元件,或從eureka獲取元件呼叫地址。

二.存在問題
基於如上微服務的分散式架構如果按照傳統方式,將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。

5.具體實現
1)通過springframework的EnableRedisHttpSession註解管理session,zuul和micro service元件實現這個類以儲存、獲取redis中儲存的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。

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服務地址,呼叫登陸介面。


2)檢視各元件sessionId

zuul sessionId:


micro service1 sessionId:


micro service1呼叫micro service2 sessionId:


結論:可以看到zuul和micro service中sessionId都是相同的,都是586b*c9a4,通過這種方式實現了API呼叫過程中的session物件共享。