這篇文章是鬆哥的原創,可是在第一次發佈的時候,忘了標記原創,結果被好多號轉發,致使我後來整理的時候本身無法標記原創了。寫了幾百篇原創技術乾貨了,有一兩篇忘記標記原創進而形成的一點點小小損失也能接受,不過仍是要和小夥伴們說明一下。
在傳統的單服務架構中,通常來講,只有一個服務器,那麼不存在 Session 共享問題,可是在分佈式/集羣項目中,Session 共享則是一個必須面對的問題,先看一個簡單的架構圖:java
在這樣的架構中,會出現一些單服務中不存在的問題,例如客戶端發起一個請求,這個請求到達 Nginx 上以後,被 Nginx 轉發到 Tomcat A 上,而後在 Tomcat A 上往 session 中保存了一份數據,下次又來一個請求,這個請求被轉發到 Tomcat B 上,此時再去 Session 中獲取數據,發現沒有以前的數據。對於這一類問題的解決,思路很簡單,就是將各個服務之間須要共享的數據,保存到一個公共的地方(主流方案就是 Redis):nginx
當全部 Tomcat 須要往 Session 中寫數據時,都往 Redis 中寫,當全部 Tomcat 須要讀數據時,都從 Redis 中讀。這樣,不一樣的服務就可使用相同的 Session 數據了。git
這樣的方案,能夠由開發者手動實現,即手動往 Redis 中存儲數據,手動從 Redis 中讀取數據,至關於使用一些 Redis 客戶端工具來實現這樣的功能,毫無疑問,手動實現工做量仍是蠻大的。github
一個簡化的方案就是使用 Spring Session 來實現這一功能,Spring Session 就是使用 Spring 中的代理過濾器,將全部的 Session 操做攔截下來,自動的將數據 同步到 Redis 中,或者自動的從 Redis 中讀取數據。web
對於開發者來講,全部關於 Session 同步的操做都是透明的,開發者使用 Spring Session,一旦配置完成後,具體的用法就像使用一個普通的 Session 同樣。redis
首先 建立一個 Spring Boot 工程,引入 Web、Spring Session 以及 Redis:spring
建立成功以後,pom.xml 文件以下:服務器
<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>
注意:session
這裏我使用的 Spring Boot 版本是 2.1.4 ,若是使用當前最新版 Spring Boot2.1.5 的話,除了上面這些依賴以外,須要額外添加 Spring Security 依賴(其餘操做不受影響,僅僅只是多了一個依賴,固然也多了 Spring Security 的一些默認認證流程)。架構
spring.redis.host=192.168.66.128 spring.redis.port=6379 spring.redis.password=123 spring.redis.database=0
這裏的 Redis ,我雖然配置了四行,可是考慮到端口默認就是 6379 ,database 默認就是 0,因此真正要配置的,其實就是兩行。
配置完成後 ,就可使用 Spring Session 了,其實就是使用普通的 HttpSession ,其餘的 Session 同步到 Redis 等操做,框架已經自動幫你完成了:
@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; } }
考慮到一會 Spring Boot 將以集羣的方式啓動 ,爲了獲取每個請求究竟是哪個 Spring Boot 提供的服務,須要在每次請求時返回當前服務的端口號,所以這裏我注入了 server.port 。
接下來 ,項目打包:
打包以後,啓動項目的兩個實例:
java -jar sessionshare-0.0.1-SNAPSHOT.jar --server.port=8080 java -jar sessionshare-0.0.1-SNAPSHOT.jar --server.port=8081
而後先訪問 localhost:8080/set
向 8080
這個服務的 Session
中保存一個變量,訪問完成後,數據就已經自動同步到 Redis
中 了 :
而後,再調用 localhost:8081/get
接口,就能夠獲取到 8080
服務的 session
中的數據:
此時關於 session 共享的配置就已經所有完成了,session 共享的效果咱們已經看到了,可是每次訪問都是我本身手動切換服務實例,所以,接下來咱們來引入 Nginx ,實現服務實例自動切換。
很簡單,進入 Nginx 的安裝目錄的 conf 目錄下(默認是在 /usr/local/nginx/conf
),編輯 nginx.conf 文件:
在這段配置中:
/
表示攔截到全部的請求,轉發轉發到剛剛配置好的服務集羣中配置完成後,將本地的 Spring Boot 打包好的 jar 上傳到 Linux ,而後在 Linux 上分別啓動兩個 Spring Boot 實例:
nohup java -jar sessionshare-0.0.1-SNAPSHOT.jar --server.port=8080 & nohup java -jar sessionshare-0.0.1-SNAPSHOT.jar --server.port=8081 &
其中
配置完成後,重啓 Nginx:
/usr/local/nginx/sbin/nginx -s reload
Nginx 啓動成功後,咱們首先手動清除 Redis 上的數據,而後訪問 192.168.66.128/set
表示向 session
中保存數據,這個請求首先會到達 Nginx
上,再由 Nginx
轉發給某一個 Spring Boot
實例:
如上,表示端口爲 8081
的 Spring Boot
處理了這個 /set
請求,再訪問 /get
請求:
能夠看到,/get
請求是被端口爲 8080 的服務所處理的。
本文主要向你們介紹了 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。
好了 ,本文就說到這裏,本文相關案例我已經上傳到 GitHub ,你們能夠自行下載:https://github.com/lenve/javaboy-code-samples