真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么解決redis中分布式session不一致性

這篇文章主要講解了“怎么解決redis中分布式session不一致性”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么解決redis中分布式session不一致性”吧!

目前成都創(chuàng)新互聯(lián)已為千余家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬空間、網(wǎng)站托管、服務(wù)器托管、企業(yè)網(wǎng)站設(shè)計、龍井網(wǎng)站維護(hù)等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

怎么解決redis中分布式session不一致性

分布式session不一致性解決方案

一、Session有什么作用?

  • Session 是客戶端與服務(wù)器通訊會話跟蹤技術(shù),服務(wù)器與客戶端保持整個通訊的會話基本信息?!鞠嚓P(guān)推薦:Redis視頻教程】

  • 客戶端在第一次訪問服務(wù)端的時候,服務(wù)端會響應(yīng)一個sessionId并且將它存入到本地cookie中,在之后的訪問會將cookie中的sessionId放入到請求頭中去訪問服務(wù)器,

  • 如果通過這個sessionId沒有找到對應(yīng)的數(shù)據(jù),那么服務(wù)器會創(chuàng)建一個新的sessionId并且響應(yīng)給客戶端。

二、分布式Session有什么問題?

單服務(wù)器web應(yīng)用中,session信息只需存在該服務(wù)器中,這是我們前幾年最常接觸的方式

但是近幾年隨著分布式系統(tǒng)的流行,單系統(tǒng)已經(jīng)不能滿足日益增長的百萬級用戶的需求,集群方式部署服務(wù)器已在很多公司運(yùn)用起來

當(dāng)高并發(fā)量的請求到達(dá)服務(wù)端的時候通過負(fù)載均衡的方式分發(fā)到集群中的某個服務(wù)器,這樣就有可能導(dǎo)致同一個用戶的多次請求被分發(fā)到集群的不同服務(wù)器上,就會出現(xiàn)取不到session數(shù)據(jù)的情況,于是session的共享就成了一個問題。

怎么解決redis中分布式session不一致性

三、服務(wù)做集群一般是怎么樣做的?

  • SpringBoot項目,那么只要改下端口號啟動幾個,然后用nginx統(tǒng)一做反向代理。

  • SpringCloud微服務(wù)項目,那么這個時候,可以使用ribbon本地負(fù)載均衡。

四、nginx負(fù)載均衡和ribbon負(fù)載均衡的區(qū)別

  • nginx做負(fù)載均衡是服務(wù)器端的負(fù)載均衡,統(tǒng)一訪問一個地址,根據(jù)負(fù)載均衡算法訪問決定訪問那一個服務(wù)器。

  • ribbon負(fù)載均衡,這是本地負(fù)載均衡(客戶端負(fù)載均衡),把提供服務(wù)的客戶端地址都緩存記錄下來,根據(jù)本地的算法實現(xiàn)負(fù)載均衡。

五、Session一致性解決方案

1. session復(fù)制(同步)

怎么解決redis中分布式session不一致性

思路:多個服務(wù)端之間相互同步session,這樣每個服務(wù)端之間都包含全部的session

優(yōu)點(diǎn):服務(wù)端支持的功能,應(yīng)用程序不需要修改代碼

缺點(diǎn):

  • session的同步需要數(shù)據(jù)傳輸,占內(nèi)網(wǎng)帶寬,有時延

  • 所有服務(wù)端都包含所有session數(shù)據(jù),數(shù)據(jù)量受內(nèi)存限制,無法水平擴(kuò)展

2. 客戶端存儲法

怎么解決redis中分布式session不一致性

思路:服務(wù)端存儲所有用戶的session,內(nèi)存占用較大,可以將session存儲到瀏覽器cookie中,每個端只要存儲一個用戶的數(shù)據(jù)了

優(yōu)點(diǎn):服務(wù)端不需要存儲

缺點(diǎn):

  • 每次http請求都攜帶session,占外網(wǎng)帶寬

  • 數(shù)據(jù)存儲在端上,并在網(wǎng)絡(luò)傳輸,存在泄漏、篡改、竊取等安全隱患

  • session存儲的數(shù)據(jù)大小和域名cookie個數(shù)都受限制的

注:該方案雖然不常用,但確實是一種思路。

3. 反向代理hash一致性

思路:服務(wù)端為了保證高可用,有多臺冗余,反向代理層能不能做一些事情,讓同一個用戶的請求保證落在一臺服務(wù)端上呢?

方案一:四層代理hash

怎么解決redis中分布式session不一致性

反向代理層使用用戶的ip來做hash,以保證同一個ip的請求落在同一個服務(wù)端上

方案二:七層代理hash

怎么解決redis中分布式session不一致性

反向代理使用http協(xié)議中的某些業(yè)務(wù)屬性來做hash,例如sid,city_id,user_id等,能夠更加靈活的實施hash策略,以保證同一個瀏覽器用戶的請求落在同一個服務(wù)器上

優(yōu)點(diǎn):

  • 只需要改nginx配置,不需要修改應(yīng)用代碼

  • 負(fù)載均衡,只要hash屬性是均勻的,多臺服務(wù)端的負(fù)載是均衡的

  • 可以支持服務(wù)端水平擴(kuò)展(session同步法是不行的,受內(nèi)存限制)

缺點(diǎn):

  • 如果服務(wù)端重啟,一部分session會丟失,產(chǎn)生業(yè)務(wù)影響,例如部分用戶重新登錄

  • 如果服務(wù)端水平擴(kuò)展,rehash后session重新分布,也會有一部分用戶路由不到正確的session

session一般是有有效期的,所有不足中的兩點(diǎn),可以認(rèn)為等同于部分session失效,一般問題不大。

對于四層hash還是七層hash,個人推薦前者:讓專業(yè)的軟件做專業(yè)的事情,反向代理就負(fù)責(zé)轉(zhuǎn)發(fā),盡量不要引入應(yīng)用層業(yè)務(wù)屬性,除非不得不這么做(例如,有時候多機(jī)房多活需要按照業(yè)務(wù)屬性路由到不同機(jī)房的服務(wù)器)。

四層、七層負(fù)載均衡的區(qū)別

4. 后端統(tǒng)一集中存儲

怎么解決redis中分布式session不一致性

優(yōu)點(diǎn):

  • 沒有安全隱患

  • 可以水平擴(kuò)展,數(shù)據(jù)庫/緩存水平切分即可

  • 服務(wù)端重啟或者擴(kuò)容都不會有session丟失

不足:增加了一次網(wǎng)絡(luò)調(diào)用,并且需要修改應(yīng)用代碼

對于db存儲還是cache,個人推薦后者:session讀取的頻率會很高,數(shù)據(jù)庫壓力會比較大。如果有session高可用需求,cache可以做高可用,但大部分情況下session可以丟失,一般也不需要考慮高可用。

總結(jié)

保證session一致性的架構(gòu)設(shè)計常見方法:

  • session同步法:多臺服務(wù)端相互同步數(shù)據(jù)

  • 客戶端存儲法 一個用戶只存儲自己的數(shù)據(jù)

  • 反向代理hash一致性 四層hash和七層hash都可以做,保證一個用戶的請求落在一臺服務(wù)端上

  • 后端統(tǒng)一存儲 服務(wù)端重啟和擴(kuò)容,session也不會丟失(推薦后端cache統(tǒng)一存儲)

六、案例實戰(zhàn):SpringSession+redis解決分布式session不一致性問題

步驟1:加入SpringSession、redis的依賴包


    org.springframework.boot
    spring-boot-starter-redis
    1.4.7.RELEASE



    org.springframework.session
    spring-session-data-redis

步驟2:配置文件

# 為某個包目錄下 設(shè)置日志
logging.level.com.ljw=debug

# 設(shè)置session的存儲方式,采用redis存儲
spring.session.store-type=redis
# session有效時長為10分鐘
server.servlet.session.timeout=PT10M

## Redis 配置
## Redis數(shù)據(jù)庫索引(默認(rèn)為0)
spring.redis.database=0
## Redis服務(wù)器地址
spring.redis.host=127.0.0.1
## Redis服務(wù)器連接端口
spring.redis.port=6379
## Redis服務(wù)器連接密碼(默認(rèn)為空)
spring.redis.password=

步驟3: 配置攔截器

@Configuration
public class SessionConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SecurityInterceptor())
                //排除攔截的2個路徑
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/logout")
                //攔截所有URL路徑
                .addPathPatterns("/**");
    }
}
@Configuration
public class SecurityInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        HttpSession session = request.getSession();
        //驗證當(dāng)前session是否存在,存在返回true true代表能正常處理業(yè)務(wù)邏輯
        if (session.getAttribute(session.getId()) != null){
            log.info("session攔截器,session={},驗證通過",session.getId());
            return true;
        }
        //session不存在,返回false,并提示請重新登錄。
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write("請登錄?。。。?!");
        log.info("session攔截器,session={},驗證失敗",session.getId());
        return false;
    }
}
  • HandlerInterceptor

    • preHandle:在業(yè)務(wù)處理器處理請求之前被調(diào)用。預(yù)處理,可以進(jìn)行編碼、安全控制、權(quán)限校驗等處理;

    • postHandle:在業(yè)務(wù)處理器處理請求執(zhí)行完成后,生成視圖之前執(zhí)行。后處理(調(diào)用了Service并返回ModelAndView,但未進(jìn)行頁面渲染),有機(jī)會修改ModelAndView

    • afterCompletion:在DispatcherServlet完全處理完請求后被調(diào)用,可用于清理資源等。返回處理(已經(jīng)渲染了頁面)

步驟4: 控制器

@RestController
@RequestMapping(value = "/user")
public class UserController {

    Map userMap = new HashMap<>();

    public UserController() {
        //初始化2個用戶,用于模擬登錄
        User u1=new User(1,"user1","user1");
        userMap.put("user1",u1);
        User u2=new User(2,"user2","user2");
        userMap.put("user2",u2);
    }

    @GetMapping(value = "/login")
    public String login(String username, String password, HttpSession session) {
        //模擬數(shù)據(jù)庫的查找
        User user = this.userMap.get(username);
        if (user != null) {
            if (!password.equals(user.getPassword())) {
                return "用戶名或密碼錯誤!??!";
            } else {
                session.setAttribute(session.getId(), user);
                log.info("登錄成功{}",user);
            }
        } else {
            return "用戶名或密碼錯誤?。?!";
        }
        return "登錄成功?。?!";
    }

    /**
     * 通過用戶名查找用戶
     */
    @GetMapping(value = "/find/{username}")
    public User find(@PathVariable String username) {
        User user=this.userMap.get(username);
        log.info("通過用戶名={},查找出用戶{}",username,user);
        return user;
    }

    /**
     *拿當(dāng)前用戶的session
     */
    @GetMapping(value = "/session")
    public String session(HttpSession session) {
        log.info("當(dāng)前用戶的session={}",session.getId());
        return session.getId();
    }

    /**
     * 退出登錄
     */
    @GetMapping(value = "/logout")
    public String logout(HttpSession session) {
        log.info("退出登錄session={}",session.getId());
        session.removeAttribute(session.getId());
        return "成功退出??!";
    }

}

步驟5: 實體類

@Data
public class User implements  Serializable{

    private int id;
    private String username;
    private String password;

    public User(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

}

步驟6:訪問測試

先登錄:http://127.0.0.1:8080/user/login?username=user1&password=user1

再查詢http://127.0.0.1:8080/user/find/user1

七、剖析SpringSession的redis原理

步驟1:分析SpringSession的redis數(shù)據(jù)結(jié)構(gòu)

127.0.0.1:6379> keys *
1) "spring:session:sessions:9889ccfd-f4c9-41e5-b9ab-a77649a7bb6a"
2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b"
3) "spring:session:expirations:1635413520000"
4) "spring:session:sessions:expires:9889ccfd-f4c9-41e5-b9ab-a77649a7bb6a"
5) "spring:session:expirations:1635412980000"
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

共同點(diǎn):3個key都是以spring:session:開頭的,代表了SpringSession的redis數(shù)據(jù)。

查詢類型

127.0.0.1:6379> type spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b
hash
127.0.0.1:6379> hgetall spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b
// session的創(chuàng)建時間
1) "creationTime"
2) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01|\xc5\xdb\xecu"
// sesson的屬性,存儲了user對象
3) "sessionAttr:d3434f61-4d0a-4687-9070-610bd7790f3b"
4) "\xac\xed\x00\x05sr\x00\x1ecom.ljw.redis.controller.User\x16\"_m\x1b\xa0W\x7f\x02\x00\x03I\x00\x02idL\x00\bpasswordt\x00\x12Ljava/lang/String;L\x00\busernameq\x00~\x00\x01xp\x00\x00\x00\x01t\x00\x05user1q\x00~\x00\x03"
//最后的訪問時間
5) "lastAccessedTime"
6) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01|\xc5\xe1\xc7\xed"
//失效時間 100分鐘
7) "maxInactiveInterval"
8) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x17p"

步驟2:分析SpringSession的redis過期策略

對于過期數(shù)據(jù),一般有三種刪除策略:

  • 定時刪除,即在設(shè)置鍵的過期時間的同時,創(chuàng)建一個定時器, 當(dāng)鍵的過期時間到來時,立即刪除。

  • 惰性刪除,即在訪問鍵的時候,判斷鍵是否過期,過期則刪除,否則返回該鍵值。

  • 定期刪除,即每隔一段時間,程序就對數(shù)據(jù)庫進(jìn)行一次檢查,刪除里面的過期鍵。至于要刪除多少過期鍵,以及要檢查多少個數(shù)據(jù)庫,則由算法決定。

  • redis刪除過期數(shù)據(jù)采用的是懶性刪除+定期刪除組合策略,也就是數(shù)據(jù)過期了并不會及時被刪除。

  • 但由于redis是單線程,并且redis對刪除過期的key優(yōu)先級很低;如果有大量的過期key,就會出現(xiàn)key已經(jīng)過期但是未刪除。

  • 為了實現(xiàn) session 過期的及時性,spring session 采用了定時刪除+惰性刪除的策略。

定時刪除

127.0.0.1:6379> type spring:session:expirations:1635413520000
set
127.0.0.1:6379> smembers  spring:session:expirations:1635413520000
1) "\xac\xed\x00\x05t\x00,expires:d3434f61-4d0a-4687-9070-610bd7790f3b"
2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 
3) "spring:session:expirations:1635413520000" 
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"
  • 1635412980000 是時間戳,等于 2021-10-28 17:23:00,即是該可以在這個時刻過期

  • springsession 定時(1分鐘)輪詢,刪除spring:session:expirations:[?] 的過期成員元素,例如:spring:session:expirations:1635413520000

  • springsesion 定時檢測超時的key的值,根據(jù)值刪除seesion,例如key:spring:session:expirations:1635413520000,值為(sessionId):d3434f61-4d0a-4687-9070-610bd7790f3b的seesion

惰性刪除

127.0.0.1:6379> type spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
string
127.0.0.1:6379> get spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
""
127.0.0.1:6379> ttl spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
(integer) 3143
127.0.0.1:6379>
  • 訪問 spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b的時候,判斷key是否過期,過期則刪除,否則返回改進(jìn)的值。

  • 例如 訪問spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b的時候,判斷 ttl 是否過期,過期就直接刪除

2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 
3) "spring:session:expirations:1635413520000" 
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

感謝各位的閱讀,以上就是“怎么解決redis中分布式session不一致性”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對怎么解決redis中分布式session不一致性這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!


本文名稱:怎么解決redis中分布式session不一致性
鏈接分享:http://weahome.cn/article/jjgehg.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部