應該講解清楚,爲何要使用 Redis 進行 Session 的管理。java
Session 複製又是什麼概念。git
Spring Session 在汪雲飛老師的書裏的介紹是:github
Spring Session:提供一個 API 及實現來管理用戶會話信息。web
咱們能夠這樣理解,原來在 Tomcat 服務器中存在的 Session 統一放置在 Redis 中進行管理。
這樣在服務器集羣的時候,就不會出現狀態不一致的狀況。(這一步還需要描述詳細一些,最好把本身實驗的過程展現一下)。redis
除了 SpringBoot 最最基礎的依賴 spring-boot-starter-web
spring
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.2.RELEASE'
之外,咱們還需要添加 spring-boot-starter-data-redis
和 spring-session
shell
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '1.5.2.RELEASE' compile group: 'org.springframework.session', name: 'spring-session', version: '1.3.0.RELEASE'
另外,咱們還需要 Redis 服務的支持。apache
spring.session.store-type=redis spring.redis.host=localhost spring.redis.port=6388
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 24 * 60 * 60) public class SessionConfig { }
下面編寫測試代碼。緩存
@RestController public class SessionController { // 用於測試 SpringBoot 容器是否啓動 // http:localhost:8080/test @RequestMapping("/test") public String test(){ return "PING OK"; } // http:localhost:8080/put?key=name&value=liwei @RequestMapping("/put") public String put(HttpSession session, @RequestParam("key") String key,@RequestParam("value") String value){ session.setAttribute(key,value); return "PUT OK"; } // http:localhost:8080/get?key=name @RequestMapping("/get") public String get(HttpSession session, @RequestParam("key") String key){ String value = (String) session.getAttribute(key); if(value == null || "".equals(value)){ return "NO VALUE GET"; } return value; } }
咱們執行tomcat
gradle clean build
而後再執行
java -jar build/libs/SpringBootSessionDemo-1.0-SNAPSHOT.jar --server.port=8081 java -jar build/libs/SpringBootSessionDemo-1.0-SNAPSHOT.jar --server.port=8082 java -jar build/libs/SpringBootSessionDemo-1.0-SNAPSHOT.jar --server.port=8083
啓動 3 臺服務器。
實驗的思路是這樣的:在 1 臺服務器上的 Session 中放入 key 和 value,在另外兩臺服務器上均可以得到 key 對應的 value。
第 1 步:請求
http://localhost:8081/put?key=age&value=24
第 2 步:
http://localhost:8082/get?key=age
http://localhost:8083/get?key=age
此時,在兩臺服務器 8082 和 8083 上能夠看到咱們在服務器 8081 的 Session 中放入的數據。
第 3 步:登陸 Redis 服務端,狀況 Redis 的緩存,再次執行第 2 步的請求,發現不能得到 Session 的數據。
redis-cli -p 6388 flushdb
至此,就說明此時 3 臺服務器上的 Session 都由 Redis 來管理了。
若是咱們把關於 Spring Session 和 Redis 的配置都去掉的話,就會發如今 1 臺服務器上的 Session 的設置和獲取都只能在這臺服務器上完成。
補充說明:在 Gradle 項目中要經過使用 spring-boot 這個插件,才能使打出來的 jar 包能夠執行 java -jar
開始運行。
配置方法:
allprojects { repositories { maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } } } buildscript { ext { springBootVersion = '1.5.2.RELEASE' } repositories { jcenter() mavenLocal() mavenCentral() maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } maven { url "http://repo.spring.io/release" } maven { url "http://repo.spring.io/milestone" } maven { url "http://repo.spring.io/snapshot" } } dependencies { classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" classpath "com.github.adrianbk:gradle-jvmsrc-plugin:0.6.1" } }
此時,就能夠使用 spring-boot 插件了。
apply plugin: 'spring-boot'
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.liwei.model.User] at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:93) ~[spring-data-redis-1.8.1.RELEASE.jar!/:na] at org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:171) ~[spring-data-redis-1.8.1.RELEASE.jar!/:na] at org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:129) ~[spring-data-redis-1.8.1.RELEASE.jar!/:na] at org.springframework.data.redis.core.DefaultBoundHashOperations.putAll(DefaultBoundHashOperations.java:86) ~[spring-data-redis-1.8.1.RELEASE.jar!/:na] at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:778) ~[spring-session-1.3.0.RELEASE.jar!/:na] at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:670) ~[spring-session-1.3.0.RELEASE.jar!/:na] at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:388) ~[spring-session-1.3.0.RELEASE.jar!/:na] at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:245) ~[spring-session-1.3.0.RELEASE.jar!/:na] at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:245) ~[spring-session-1.3.0.RELEASE.jar!/:na] at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:217) ~[spring-session-1.3.0.RELEASE.jar!/:na] at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:170) ~[spring-session-1.3.0.RELEASE.jar!/:na] at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:80) ~[spring-session-1.3.0.RELEASE.jar!/:na] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.7.RELEASE.jar!/:4.3.7.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.7.RELEASE.jar!/:4.3.7.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:474) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:783) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:798) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1434) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_45] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_45] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.11.jar!/:8.5.11] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45] Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.liwei.model.User] at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:68) ~[spring-core-4.3.7.RELEASE.jar!/:4.3.7.RELEASE] at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:35) ~[spring-core-4.3.7.RELEASE.jar!/:4.3.7.RELEASE] at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:91) ~[spring-data-redis-1.8.1.RELEASE.jar!/:na] ... 33 common frames omitted Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.liwei.model.User] at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:43) ~[spring-core-4.3.7.RELEASE.jar!/:4.3.7.RELEASE] at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:63) ~[spring-core-4.3.7.RELEASE.jar!/:4.3.7.RELEASE] ... 35 common frames omitted
User 類要實現序列化接口。
http://localhost:8081/put/user
http://localhost:8082/put/user
http://localhost:8083/put/user
這篇文章涉及到的源代碼能夠在個人 GitHub 上下載:https://github.com/weimingge14/SpringBootSessionDemo