亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Java分布式系統(tǒng)中session一致性問題

 更新時(shí)間:2021年04月22日 16:13:18   作者:JiaJian  
這篇文章主要介紹了Java分布式系統(tǒng)中session一致性問題,對(duì)分布式系統(tǒng)感興趣的同學(xué),要仔細(xì)看一下

業(yè)務(wù)場(chǎng)景

在單機(jī)系統(tǒng)中,用戶登陸之后,服務(wù)端會(huì)保存用戶的會(huì)話信息,只要用戶不退出重新登陸,在一段時(shí)間內(nèi)用戶可以一直訪問該網(wǎng)站,無需重復(fù)登陸。用戶的信息存在服務(wù)端的 session 中,session中可以存放服務(wù)端需要的一些用戶信息,例如用戶ID,所屬公司companyId,所屬部門deptId等等。

但是隨著業(yè)務(wù)的發(fā)展,技術(shù)架構(gòu)需要調(diào)整,原來的單機(jī)系統(tǒng)逐漸被更換,架構(gòu)由單機(jī)擴(kuò)展到分布式,甚至當(dāng)下流行的微服務(wù)。雖然在用戶端看來系統(tǒng)仍然是一個(gè)整體,但在技術(shù)端來說業(yè)務(wù)則被拆分成多個(gè)模塊,各個(gè)模塊之間相互獨(dú)立,甚至不在同一臺(tái)物理機(jī)器上,模塊之間通過 RPC 進(jìn)行通信。

那么原來單機(jī)只需一份的 session, 如何滿足在多系統(tǒng)的運(yùn)行下保證會(huì)話一致性呢?單獨(dú)保存在任何一個(gè)系統(tǒng)中都不合適,而且每個(gè)單獨(dú)模塊系統(tǒng)也可能是分布式形式的,是由集群組成。那么session的分配就更復(fù)雜了。

Redis 實(shí)現(xiàn)

針對(duì)以上問題,我們可能會(huì)從以下幾個(gè)方面想到解決的方法,每個(gè)服務(wù)端存儲(chǔ)一份,通過同步的方式保證一致性,但是這種方式有個(gè)很明顯的缺點(diǎn):session的同步需要數(shù)據(jù)傳輸,占內(nèi)網(wǎng)帶寬,有時(shí)延,網(wǎng)絡(luò)不穩(wěn)定的時(shí)候會(huì)造成部分系統(tǒng)同步延遲,那么就不能保證 session 一致性。而且所有服務(wù)端都包含所有session數(shù)據(jù),數(shù)據(jù)量受內(nèi)存限制,無法水平擴(kuò)展。

那么我們是否可以單獨(dú)將 session 信息存儲(chǔ)在某一個(gè)獨(dú)立的介質(zhì)中,介質(zhì)可以是DB也可以是緩存。

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

我們只需在用戶首次登陸的時(shí)候?qū)⒂脩粜畔⒎诺?Token并緩存到 Redis 中,同時(shí)設(shè)置一個(gè)過期時(shí)間,偽代碼如下:

@Override
public Map login(UserDto dto) {
    Map<String, Object> restMap = new HashMap<>();
    
    // 校驗(yàn)登陸信息
    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);
    // 設(shè)置用戶 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;
}

那么在系統(tǒng)中如何使用呢,我們可以定義一個(gè)攔截器 SessionInterceptor,當(dāng)訪問 web 接口的時(shí)候檢驗(yàn)用戶的 token 信息,判斷用戶是否登陸,未登錄的情況下一些業(yè)務(wù)接口是無法訪問的,以及在登陸的情況下拿到我們需要的用戶信息,如 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("驗(yàn)證token", "token驗(yàn)證失敗,{}", "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("驗(yàn)證token", "token驗(yàn)證失敗,{}", "token超時(shí)");
            throw new FieldException(Constants.LOGIN_ERROR_CODE, "login.session.timeout");
        }
        User user = userService.getById(userId.longValue());
        if (ObjectUtils.isEmpty(user)){
            Log.debug("驗(yàn)證token", "token驗(yàn)證失敗,{}", "用戶信息不存在");
            throw new FieldException(Constants.LOGIN_ERROR_CODE, "login.session.timeout");
        }
        if (user.getStatus() == UserStatusEnum.NO.getCode() || user.getDeleteFlag() == DeleteFlagEnum.YES.getCode()){
            Log.debug("驗(yàn)證token", "token驗(yàn)證失敗,用戶信息異常 userName : {}, status : {},deleteFlag : {}", user.getUserName(),user.getStatus(), user.getDeleteFlag());
            throw new FieldException(Constants.LOGIN_ERROR_CODE, "login.session.timeout");
        }
        return proceedingJoinPoint.proceed();
    }
    
}

以上實(shí)現(xiàn)方式簡單易用,而且Redis 在分布式系統(tǒng)中的使用率也很高,所以無需額外的技術(shù)引入??梢灾С炙綌U(kuò)展,數(shù)據(jù)庫或緩存水平切分即可,服務(wù)端重啟或者擴(kuò)容都不會(huì)有session丟失的情況發(fā)生。

以上就是詳解Java分布式系統(tǒng)中session一致性問題的詳細(xì)內(nèi)容,更多關(guān)于Java分布式系統(tǒng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Mybatis基于TypeHandler實(shí)現(xiàn)敏感數(shù)據(jù)加密

    Mybatis基于TypeHandler實(shí)現(xiàn)敏感數(shù)據(jù)加密

    業(yè)務(wù)場(chǎng)景中經(jīng)常會(huì)遇到諸如用戶手機(jī)號(hào),身份證號(hào),銀行卡號(hào),郵箱,地址,密碼等等信息,屬于敏感信息,本文就來介紹一下Mybatis基于TypeHandler實(shí)現(xiàn)敏感數(shù)據(jù)加密,感興趣的可以了解一下
    2023-10-10
  • 使用SpringBoot2.x配置靜態(tài)文件緩存

    使用SpringBoot2.x配置靜態(tài)文件緩存

    這篇文章主要介紹了使用SpringBoot2.x配置靜態(tài)文件緩存的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • java開發(fā)RocketMQ生產(chǎn)者高可用示例詳解

    java開發(fā)RocketMQ生產(chǎn)者高可用示例詳解

    這篇文章主要為大家介紹了java開發(fā)RocketMQ生產(chǎn)者高可用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 全解史上最快的JOSN解析庫alibaba Fastjson

    全解史上最快的JOSN解析庫alibaba Fastjson

    這篇文章主要介紹了史上最快的JOSN解析庫alibaba Fastjson,對(duì)FastJson感興趣的同學(xué),一定要看一下
    2021-04-04
  • Java如何實(shí)現(xiàn)讀取txt文件內(nèi)容并生成Word文檔

    Java如何實(shí)現(xiàn)讀取txt文件內(nèi)容并生成Word文檔

    本文主要介紹了通過Java實(shí)現(xiàn)讀取txt文件中的內(nèi)容,并將內(nèi)容生成Word文檔。文章的代碼非常詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下
    2021-12-12
  • MyBatis快速入門之環(huán)境搭建和單表映射

    MyBatis快速入門之環(huán)境搭建和單表映射

    一說起對(duì)象關(guān)系映射框架,大家第一時(shí)間想到的肯定是Hibernate。Hibernate作為一個(gè)著名的框架,功能十分強(qiáng)大。但是由于Hibernate如此強(qiáng)大的功能,導(dǎo)致了它的缺點(diǎn)。好吧,不多說了,具體詳情大家通過本文一起學(xué)習(xí)吧
    2017-03-03
  • 200行Java代碼編寫一個(gè)計(jì)算器程序

    200行Java代碼編寫一個(gè)計(jì)算器程序

    本篇文章給大家分享的只用200行java代碼,實(shí)現(xiàn)一個(gè)計(jì)算器程序,不僅能夠計(jì)算加減乘除,還能夠匹配小括號(hào)。實(shí)現(xiàn)代碼超簡單,需要的朋友參考下吧
    2017-12-12
  • Java實(shí)現(xiàn)JS中的escape和UNescape代碼分享

    Java實(shí)現(xiàn)JS中的escape和UNescape代碼分享

    在PHP和Python中都有類似JS中的escape和UNescape函數(shù)的功能,那么Java語言中到底有沒有類似的方法呢?本文就來介紹一下Java實(shí)現(xiàn)JS中的escape和UNescape轉(zhuǎn)碼方法,需要的朋友可以參考下
    2017-09-09
  • springmvc的文件保存方法詳解

    springmvc的文件保存方法詳解

    這篇文章主要介紹了springmvc的文件保存方法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Javas使用Redlock實(shí)現(xiàn)分布式鎖過程解析

    Javas使用Redlock實(shí)現(xiàn)分布式鎖過程解析

    這篇文章主要介紹了Javas使用Redlock實(shí)現(xiàn)分布式鎖過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08

最新評(píng)論