Spring Boot 2 + Redis 處理 Session 共享

架構師springboot發表於2019-03-23
Spring Boot 2 + Redis 處理 Session 共享

〇、背景

Web 開發中,通過 Session 在服務端記錄使用者狀態是很常見的操作。對於 Web 開發中 Session、Cookie 等概念請參考《Session 機制詳解》。但是 Session 的機制對於單機應用是沒問題的,但是對於叢集環境,由於在將請求分配到另一臺伺服器時,新的伺服器無法通過瀏覽器傳入的 Cookie 值取到 Session,所以導致所有基於 Session 的操作都會失敗,如:登入狀態。
  本文通過搭建一個非常簡易的叢集環境,來演示 Session 機制在叢集環境中存在的問題,並通過 Redis 進行 Session 共享來解決該問題。

一、問題再現

1、測試環境

(1)App Server

使用 Spring Boot 2 寫一個簡單的 Web 應用,提供兩個連結:

Spring Boot 2 + Redis 處理 Session 共享

Controller 部分程式碼如下:

@RestController
public class TestController {

    @GetMapping("/set-session")
    public Object writeSession(String sessionVal, HttpSession httpSession) {
        System.out.println("Param 'sessionVal' = " + sessionVal);
        httpSession.setAttribute("sessionVal", sessionVal);
        return sessionVal;
    }

    @GetMapping("/get-session")
    public Object readSession(HttpSession httpSession) {
        Object obj = httpSession.getAttribute("sessionVal");
        System.out.println("'sessionVal' in Session = " + obj);
        return obj;
    }
    
}
複製程式碼

單機測試通過。

(2)通過 Nginx 做負載均衡

分別在 9001 和 9002 兩個埠啟動 App Server,然後通過 Nginx 配置負載均衡,配置如下:

http {

    upstream app_server {
        server 127.0.0.1:9001;
        server 127.0.0.1:9002;
    }

    server {
        listen 9000;
        location / {
            proxy_pass http://app_server;
        }
    }
}
複製程式碼

測試失敗。

二、原因分析

主要是因為原來 A 伺服器將其 Session 的標識 Cookie_for_Session_A 放入瀏覽器 Cookie,當下一次請求被分配到 B 伺服器,B 伺服器無法通過 Cookie_for_Session_A 獲取到對應的 Session,導致失敗。

解決的思路,主要是引入三方伺服器,將 Session 儲存到三方伺服器,A、B 伺服器共享三方伺服器中的 Session 資料。

三、解決方案

引入 Redis 作為三方伺服器儲存 Session 資料。

1、引入 Redis 相關庫

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.boot:spring-boot-starter-data-redis')

    // https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis
    compile group: 'org.springframework.session', name: 'spring-session-data-redis', version: '2.1.2.RELEASE'

}
複製程式碼

2、配置 Redis 連線

application.yml,這裡為了演示清晰,只做了最簡配置,正式使用請調整相關引數

spring:
  redis:
    host: 127.0.0.1
    port: 6379
複製程式碼

3、開啟配置

建立一個配置類 SessionConfig,類名隨意。
關鍵是兩個註解:

  • @Configuration
  • @EnableRedisHttpSession
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class SessionConfig {

}
複製程式碼

4、打包、執行測試

執行 Gradle 的 bootJar 任務,然後按照前面的方式,分別在 9001 和 9002 埠執行 jar 包:

java -jar redis-session.jar --server.port=9001
java -jar redis-session.jar --server.port=9002
複製程式碼

測試通過。

感興趣的可以自己來我的Java架構群,可以獲取免費的學習資料,群號:855801563對Java技術,架構技術感興趣的同學,歡迎加群,一起學習,相互討論。


相關文章