1. 程式人生 > 實用技巧 >springboot(十八)-session共享

springboot(十八)-session共享

前言

在傳統的單服務架構中,一般來說,只有一個伺服器,那麼不存在 Session 共享問題,但是在分散式/叢集專案中,Session 共享則是一個必須面對的問題,先看一個簡單的架構圖:

在這樣的架構中,會出現一些單服務中不存在的問題,例如客戶端發起一個請求,這個請求到達 Nginx 上之後,被 Nginx 轉發到 Tomcat A 上,然後在 Tomcat A 上往 session 中儲存了一份資料,下次又來一個請求,這個請求被轉發到 Tomcat B 上,此時再去 Session 中獲取資料,發現沒有之前的資料。對於這一類問題的解決,思路很簡單,就是將各個服務之間需要共享的資料,儲存到一個公共的地方(主流方案就是 Redis):

當所有 Tomcat 需要往 Session 中寫資料時,都往 Redis 中寫,當所有 Tomcat 需要讀資料時,都從 Redis 中讀。這樣,不同的服務就可以使用相同的 Session 資料了。

這樣的方案,可以由開發者手動實現,即手動往 Redis 中儲存資料,手動從 Redis 中讀取資料,相當於使用一些 Redis 客戶端工具來實現這樣的功能,毫無疑問,手動實現工作量還是蠻大的。

一個簡化的方案就是使用 Spring Session 來實現這一功能,Spring Session 就是使用 Spring 中的代理過濾器,將所有的 Session 操作攔截下來,自動的將資料 同步到 Redis 中,或者自動的從 Redis 中讀取資料。

對於開發者來說,所有關於 Session 同步的操作都是透明的,開發者使用 Spring Session,一旦配置完成後,具體的用法就像使用一個普通的 Session 一樣。

實戰

1.建立一個springboot專案,新增依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.javaboy</groupId>
    <artifactId>sessionshare</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sessionshare</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.配置application.properties或者.yml檔案

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0

server.port=8082

我們再這個檔案中配置下redis。

3.提供介面檔案

package org.javaboy.sessionshare;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@RestController
public class HelloController {
    @Value("${server.port}")
    Integer port;

    @GetMapping("/set")
    public String set(HttpSession session) {
        session.setAttribute("user", "javaboy");
        return String.valueOf(port);
    }

    @GetMapping("/get")
    public String get(HttpSession session) {
        return session.getAttribute("user") + ":" + port;
    }
}

簡單說下,@Value("{server.port}") 如果我們不指定埠,直接啟動該例項,那麼port = 8082 (application.properties檔案中有寫),如果我們指定埠啟動該例項,那port就是我們指定的埠了。具體的用法下面有。

我們只提供兩個介面,一個set和一個get。字面上就能明白,不做贅言。

4.啟動類

package org.javaboy.sessionshare;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SessionshareApplication {

    public static void main(String[] args) {
        SpringApplication.run(SessionshareApplication.class, args);
    }

}

測試

啟動redis服務

我們在終端cd 到專案目錄

  mvn install

  java -jar target/sessionshare-0.0.1-SNAPSHOT.jar--server.port=8080

再啟動一個終端cd 到專案目錄

  java -jar target/sessionshare-0.0.1-SNAPSHOT.jar--server.port=8081

指定埠啟動兩個實列。

瀏覽器訪問 localhost:8080/set , 你會看到頁面上顯示8080,我們已經set完了。資料已經儲存到redis中。

然後訪問 localhost:8081/get , 看到頁面上顯示

javaboy:8081

說明我們取到了redis中的資料。

此時關於 session 共享的配置就已經全部完成了,session 共享的效果我們已經看到了,但是每次訪問都是我自己手動切換服務例項,因此,接下來我們來引入 Nginx ,實現服務例項自動切換。

引入 Nginx

編輯 nginx.conf 檔案:

upstream springboot{
    server 127.0.0.1:8080 weight=1;
    server 127.0.0.1:8081 weight=2;
    }

    server {
        listen       9000;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            proxy_pass http://springboot;
        }

在這段配置中:

  1. upstream 表示配置上游伺服器
  2. springboot表示伺服器叢集的名字,這個可以隨意取名字
  3. upstream 裡邊配置的是一個個的單獨服務
  4. weight 表示服務的權重,意味者將有多少比例的請求從 Nginx 上轉發到該服務上
  5. location 中的 proxy_pass 表示請求轉發的地址,/表示攔截到所有的請求,轉發轉發到剛剛配置好的服務叢集中

  

配置完成後,啟動nginx,和上面兩個springboot例項。

因為我的nginx埠是9000,且在本地,

多次訪問localhost:9000/set,多次訪問localhost:9000/get.

看頁面結果,我們即實現了redis中儲存和讀取session中的值,也實現了負載均衡。

2 總結

本文主要向大家介紹了 Spring Session 的使用,另外也涉及到一些 Nginx 的使用 ,雖然本文較長,但是實際上 Spring Session 的配置沒啥。

我們寫了一些程式碼,也做了一些配置,但是全都和 Spring Session 無關,配置是配置 Redis,程式碼就是普通的 HttpSession,和 Spring Session 沒有任何關係!

唯一和 Spring Session 相關的,可能就是我在一開始引入了 Spring Session 的依賴吧!

如果大家沒有在 SSM 架構中用過 Spring Session ,可能不太好理解我們在 Spring Boot 中使用 Spring Session 有多麼方便,因為在 SSM 架構中,Spring Session 的使用要配置三個地方 ,一個是 web.xml 配置代理過濾器,然後在 Spring 容器中配置 Redis,最後再配置 Spring Session,步驟還是有些繁瑣的,而 Spring Boot 中直接幫我們省去了這些繁瑣的步驟!不用再去配置 Spring Session。

程式碼下載地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/sessionshare.zip