javascript
Spring Session - 使用Spring Session从零到一构建分布式session
文章目錄
- 快速入門 Spring Session + Redis
- 官網指導
- Demo
- pom 依賴
- 配置文件
- 配置類RedisHttpSessionConfiguration
- Redis中的session數據解析
- 附 其他相關類
- BaseController
- 統一返回結果相關的Code
- Jedis的POM依賴及配置
快速入門 Spring Session + Redis
官網指導
https://spring.io/projects/spring-session-data-redis#samples
我們就用spring boot 來演示下吧
Demo
pom 依賴
<?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"><parent><artifactId>boot2</artifactId><groupId>com.artisan</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>springsession</artifactId><dependencies><!-- 實現對 Spring MVC 的自動化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- redis lettuce 需要使用--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!-- 實現對 Spring Session 使用 Redis 作為數據源的自動化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 實現對 Spring Data Redis 的自動化配置 --><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>配置文件
server:port: 8888spring:redis:host: 127.0.0.1port: 6379password: # Redis密碼timeout: 5000mslettuce:pool:max-active: 8max-wait: -1msmax-idle: 8min-idle: 0session:store-type: redis-
max-active: 8 # 連接池最大連接數,默認為 8 。使用負數表示沒有限制。
-
max-idle: 8 # 默認連接數最大空閑的連接數,默認為 8 。使用負數表示沒有限制。
-
min-idle: 0 # 默認連接池最小空閑的連接數,默認為 0 。允許設置 0 和 正數。
-
max-wait: -1 # 連接池最大阻塞等待時間,單位:毫秒。默認為 -1 ,表示不限制。
-
session: store-type: redis 指定存儲類型
配置類RedisHttpSessionConfiguration
package com.artisan.config;import org.springframework.context.annotation.Configuration; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/2/16 14:12* @mark: show me the code , change the world*/ @Configuration @EnableRedisHttpSession public class RedisHttpSessionConfiguration {@Bean(name = "springSessionDefaultRedisSerializer")public RedisSerializer springSessionDefaultRedisSerializer() {return RedisSerializer.json();} }添加 @EnableRedisHttpSession 注解,開啟自動化配置 Spring Session 使用 Redis 作為數據 。
我們來下 EnableRedisHttpSession 注解
-
maxInactiveIntervalInSeconds 屬性,Session 不活躍后的過期時間,默認為 1800 秒。
-
redisNamespace 屬性,在 Redis 的 key 的統一前綴,默認為 “spring:session” 。
-
flushMode 屬性,Redis 會話刷新模式(RedisFlushMode)。支持兩種,默認為RedisFlushMode.ON_SAVE
RedisFlushMode.ON_SAVE ,在請求執行完成時,統一寫入 Redis 存儲。
RedisFlushMode.IMMEDIATE ,在每次修改 Session 時,立即寫入 Redis 存儲。 -
cleanupCron 屬性,清理 Redis Session 會話過期的任務執行 CRON 表達式,默認為 "0 * * * * *" 每分鐘執行一次。雖然Redis 自帶了 key 的過期,但是惰性刪除策略,實際過期的 Session 還在 Redis 中占用內存。所以,Spring Session 通過定時任務,刪除 Redis 中過期的 Session ,盡快釋放 Redis 的內存。
默認情況下,采用 Java 自帶的序列化方式 ,可讀性很差。 所以在 springSessionDefaultRedisSerializer() 方法,定義了一個 Bean 名字為 springSessionDefaultRedisSerializer的 RedisSerializer Bean ,采用 JSON 序列化方式 。
好了,截止到目前,核心的框架已經搭建起來了,我們來測試下
package com.artisan.controller;import com.artisan.common.CommonResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/2/16 14:42* @mark: show me the code , change the world*/ @RestController public class ArtisanController extends BaseController {@GetMapping("/mockSet")public CommonResult set(@RequestParam("key") String key, @RequestParam("value") String value) {getHttpSession().setAttribute(key, value);return CommonResult.success("成功模擬登錄");}@GetMapping("/mockGet") public CommonResult get(@RequestParam("key") String key) {return CommonResult.success( getHttpSession().getAttribute(key));}}啟動測試類,走一波
package com.artisan;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class SpringSeesionDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringSeesionDemoApplication.class, args);}}訪問 http://localhost:8888/mockSet?key=artisan &value=avalue
http://localhost:8888/mockGet?key=artisan
Redis中的session數據解析
127.0.0.1:0>keys *1) "spring:session:sessions:expires:e0dd90b9-9551-4e8a-9609-cde0758b88c2"2) "spring:session:sessions:e0dd90b9-9551-4e8a-9609-cde0758b88c2"3) "spring:session:expirations:1613470560000"每一個 Session 對應 Redis 二個 key-value 鍵值對
- 開頭:以 spring:session 開頭,可以通過 @EnableRedisHttpSession 注解的 redisNamespace 屬性配置。
- 結尾:以對應 Session 的 sessionid 結尾。
- 中間:中間分別是 "session"、"expirations"、sessions:expires
一般情況下,只需要關注中間為 session 的 key-value 鍵值對即可,它負責真正存儲 Session 數據
127.0.0.1:0>hgetall spring:session:sessions:ab7d40d8-cd3d-49d7-8b3a-d1ae71d409351) "lastAccessedTime" # 最后訪問時間2) "1613469344975"3) "maxInactiveInterval" # Session 允許最大不活躍時長,單位:秒。4) "1800"5) "creationTime" # 創建時間6) "1613469342207"7) "sessionAttr:artisan" # 設置的屬性值8) ""avalue"" 127.0.0.1:0>對于中間為 sessions:expires和 expirations的兩個來說,主要為了實現主動刪除 Redis 過期的 Session 會話,解決 Redis 惰性刪除的問題。
spring:session:expirations:{時間戳},是為了獲得每分鐘需要過期的 sessionid 集合,即 {時間戳} 是每分鐘的時間戳
附 其他相關類
BaseController
package com.artisan.controller;import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;public class BaseController {public HttpServletRequest getRequest(){return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();}public HttpServletResponse getResponse(){return ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();}public HttpSession getHttpSession(){return getRequest().getSession();}}統一返回結果相關的Code
【IErrorCode 】
package com.artisan.common;public interface IErrorCode {long getCode();String getMessage(); }【ResultCode 】
package com.artisan.common;public enum ResultCode implements IErrorCode {SUCCESS(200, "操作成功"),FAILED(500, "操作失敗"),VALIDATE_FAILED(404, "參數檢驗失敗"),UNAUTHORIZED(401, "暫未登錄或token已經過期"),FORBIDDEN(403, "沒有相關權限");private long code;private String message;private ResultCode(long code, String message) {this.code = code;this.message = message;}public long getCode() {return code;}public String getMessage() {return message;} }【CommonResult】
package com.artisan.common;public class CommonResult<T> {private long code;private String message;private T data;protected CommonResult() {}protected CommonResult(long code, String message, T data) {this.code = code;this.message = message;this.data = data;}/*** 成功返回結果** @param data 獲取的數據*/public static <T> CommonResult<T> success(T data) {return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);}/*** 成功返回結果** @param data 獲取的數據* @param message 提示信息*/public static <T> CommonResult<T> success(T data, String message) {return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);}/*** 失敗返回結果* @param errorCode 錯誤碼*/public static <T> CommonResult<T> failed(IErrorCode errorCode) {return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);}/*** 失敗返回結果* @param message 提示信息*/public static <T> CommonResult<T> failed(String message) {return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);}/*** 失敗返回結果*/public static <T> CommonResult<T> failed() {return failed(ResultCode.FAILED);}/*** 參數驗證失敗返回結果*/public static <T> CommonResult<T> validateFailed() {return failed(ResultCode.VALIDATE_FAILED);}/*** 參數驗證失敗返回結果* @param message 提示信息*/public static <T> CommonResult<T> validateFailed(String message) {return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);}/*** 未登錄返回結果*/public static <T> CommonResult<T> unauthorized(T data) {return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);}/*** 未授權返回結果*/public static <T> CommonResult<T> forbidden(T data) {return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);}/*** 請求異常返回結果#add by yangguo*/public static <T> CommonResult<T> badResponse(IErrorCode errorCode) {return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);}public long getCode() {return code;}public void setCode(long code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;} }Jedis的POM依賴及配置
Spring Boot 2 以上默認使用lettuce作為redis的客戶端,如果想要用jedis ,我這里也給大家準備了一份,請參考
<dependencies><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><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><!-- 去掉對 Lettuce 的依賴, Spring Boot 優先使用 Lettuce 作為 Redis 客戶端 --><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><!-- 引入 Jedis 的依賴 --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency></dependencies> spring:# 對應 RedisProperties 類redis:host: 127.0.0.1port: 6379password: # Redis 服務器密碼,默認為空。生產中,一定要設置 Redis 密碼!database: 0 # Redis 數據庫號,默認為 0 。timeout: 0 # Redis 連接超時時間,單位:毫秒。# 對應 RedisProperties.Jedis 內部類jedis:pool:max-active: 8 # 連接池最大連接數,默認為 8 。使用負數表示沒有限制。max-idle: 8 # 默認連接數最大空閑的連接數,默認為 8 。使用負數表示沒有限制。min-idle: 0 # 默認連接池最小空閑的連接數,默認為 0 。允許設置 0 和 正數。max-wait: -1 # 連接池最大阻塞等待時間,單位:毫秒。默認為 -1 ,表示不限制。總結
以上是生活随笔為你收集整理的Spring Session - 使用Spring Session从零到一构建分布式session的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Session - Co
- 下一篇: Spring Session -