91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java分布式系統中session一致性問題怎么解決

發布時間:2021-05-06 10:32:37 來源:億速云 閱讀:170 作者:小新 欄目:開發技術

小編給大家分享一下Java分布式系統中session一致性問題怎么解決,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

Java可以用來干什么

Java主要應用于:1. web開發;2. Android開發;3. 客戶端開發;4. 網頁開發;5. 企業級應用開發;6. Java大數據開發;7.游戲開發等。

業務場景

在單機系統中,用戶登陸之后,服務端會保存用戶的會話信息,只要用戶不退出重新登陸,在一段時間內用戶可以一直訪問該網站,無需重復登陸。用戶的信息存在服務端的 session 中,session中可以存放服務端需要的一些用戶信息,例如用戶ID,所屬公司companyId,所屬部門deptId等等。

Java分布式系統中session一致性問題怎么解決

但是隨著業務的發展,技術架構需要調整,原來的單機系統逐漸被更換,架構由單機擴展到分布式,甚至當下流行的微服務。雖然在用戶端看來系統仍然是一個整體,但在技術端來說業務則被拆分成多個模塊,各個模塊之間相互獨立,甚至不在同一臺物理機器上,模塊之間通過 RPC 進行通信。

Java分布式系統中session一致性問題怎么解決

那么原來單機只需一份的 session, 如何滿足在多系統的運行下保證會話一致性呢?單獨保存在任何一個系統中都不合適,而且每個單獨模塊系統也可能是分布式形式的,是由集群組成。那么session的分配就更復雜了。

Redis 實現

針對以上問題,我們可能會從以下幾個方面想到解決的方法,每個服務端存儲一份,通過同步的方式保證一致性,但是這種方式有個很明顯的缺點:session的同步需要數據傳輸,占內網帶寬,有時延,網絡不穩定的時候會造成部分系統同步延遲,那么就不能保證 session 一致性。而且所有服務端都包含所有session數據,數據量受內存限制,無法水平擴展。

那么我們是否可以單獨將 session 信息存儲在某一個獨立的介質中,介質可以是DB也可以是緩存。

考慮到如下業務:登陸的時候我們經常會給用戶一個過期時間(一般移動端常設置為7天或者一個月甚至更久),到期后用戶需要輸入登陸信息重新登陸,即會話過期。這種到期的設置我們自然想到了Redis的 key expire功能,所以最終我們可以將Redis引入進來實現我們的這種需求。系統如下圖所示:

Java分布式系統中session一致性問題怎么解決

我們只需在用戶首次登陸的時候將用戶信息放到 Token并緩存到 Redis 中,同時設置一個過期時間,偽代碼如下:

@Override
public Map login(UserDto dto) {
    Map<String, Object> restMap = new HashMap<>();
    
    // 校驗登陸信息
    User user = checkLoginInfo(dto);

     //刪除舊的token
    String token = (String) redisUtils.get(CacheConstants.USER_TOKEN_KEY_COPY + user.getUserName());
    
    if (!ObjectUtils.isEmpty(token)) {
        redisUtils.delete(CacheConstants.USER_TOKEN_KEY_WEB + token);
    }
    // 唯一簽名信息
    String signStr = user.getCompanyId() + user.getUserName() + dto.getPassword() + DateUtils.now().getTime();
    token = MD5Utils.md5(signStr);
    // 設置用戶 token
    redisUtils.setExpiredAt(CacheConstants.USER_TOKEN_KEY_WEB + token, user.getId(), LOGIN_EXPIRED_TIME);
    //緩存新的token
    redisUtils.setExpiredAt(CacheConstants.USER_TOKEN_KEY_COPY + user.getUserName(), token, LOGIN_EXPIRED_TIME);
    dto.setCompanyId(user.getCompanyId());
    dto.setId(user.getId());
    restMap.put("token", token);
    restMap.put("userName", user.getUserName());
    return restMap;
}

那么在系統中如何使用呢,我們可以定義一個攔截器 SessionInterceptor,當訪問 web 接口的時候檢驗用戶的 token 信息,判斷用戶是否登陸,未登錄的情況下一些業務接口是無法訪問的,以及在登陸的情況下拿到我們需要的用戶信息,如 userId。

public class SessionInterceptor {

    @Autowired
    private RedisUtils redisUtils;
    
    @Autowired
    private UserService userService;

    @Pointcut("execution(* com.jajian.demo.web.*.controller.*.*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void controllerMethodPointcut() {

    }

    @Around("controllerMethodPointcut()")
    public Object Interceptor(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        
        Signature signature = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        if (targetMethod.getDeclaringClass().isAnnotationPresent(NoLogin.class) || targetMethod.isAnnotationPresent(NoLogin.class)) {
            return proceedingJoinPoint.proceed();
        }
        // 從獲取RequestAttributes中獲取HttpServletRequest的信息
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        String token = request.getHeader("token");

        if(StringUtils.isEmpty(token)){
            Log.debug("驗證token", "token驗證失敗,{}", "token不存在");
            throw new FieldException(Constants.LOGIN_ERROR_CODE, "login.session.timeout");
        }
        Integer userId= (Integer)redisUtils.get(CacheConstants.USER_TOKEN_KEY_WEB + token);
       
        if (null == userId) {
            Log.debug("驗證token", "token驗證失敗,{}", "token超時");
            throw new FieldException(Constants.LOGIN_ERROR_CODE, "login.session.timeout");
        }
        User user = userService.getById(userId.longValue());
        if (ObjectUtils.isEmpty(user)){
            Log.debug("驗證token", "token驗證失敗,{}", "用戶信息不存在");
            throw new FieldException(Constants.LOGIN_ERROR_CODE, "login.session.timeout");
        }
        if (user.getStatus() == UserStatusEnum.NO.getCode() || user.getDeleteFlag() == DeleteFlagEnum.YES.getCode()){
            Log.debug("驗證token", "token驗證失敗,用戶信息異常 userName : {}, status : {},deleteFlag : {}", user.getUserName(),user.getStatus(), user.getDeleteFlag());
            throw new FieldException(Constants.LOGIN_ERROR_CODE, "login.session.timeout");
        }
        return proceedingJoinPoint.proceed();
    }
    
}

以上是“Java分布式系統中session一致性問題怎么解決”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

土默特右旗| 贵溪市| 五大连池市| 老河口市| 汾西县| 禹州市| 连城县| 贵阳市| 石嘴山市| 香格里拉县| 城固县| 黄山市| 保德县| 宁武县| 应城市| 龙泉市| 澄城县| 高安市| 容城县| 堆龙德庆县| 桑植县| 长治县| 民权县| 朝阳区| 上林县| 中西区| 安徽省| 宁远县| 凤凰县| 陆良县| 泸水县| 靖西县| 崇左市| 宝兴县| 连平县| 富源县| 双柏县| 汉中市| 北票市| 辛集市| 彰化县|