- 分布式环境下的session共享问题
- 分布式环境下的session共享解决方案
-
方案一: session同步(复制)
- 优点
tomcat原生支持,只需要修改配置文件 - 缺点
session同步需要数据传输,占用大量带宽
任意一台服务器保存的session数据都是所有服务器的session数据总和,占用内存较大
- 优点
-
方案二: 将session数据存储在客户端
- 优点
将数据存储在客户端,在需要的时候直接从客户端获取,也就没有了分布式session的问题
节省服务器端资源 - 缺点
数据存放在客户端的cookie中,存在泄漏、篡改、窃取等安全隐患
cookie保存的数据限制4K,不能保存大量信息
每次http请求都要携带cookie信息,浪费网络带宽
- 优点
-
方案三: hash一致性(可用)
- 优点
如果服务器数量不变,每次hash都会映射到相同的服务器
只需要修改nginx配置,不需要修改应用代码
负载均衡,只要hash属性的值分布是均匀的,多台web-server的负载是均衡的
可以支持web-server水平扩展 - 缺点
当web-server重启时可能会导致部分session丢失,影响业务,部分用户需要重新登录(其实该缺点问题不大,session也是有生存周期的,再获取一次即可)
如果web-server水平扩展,rehash后session重新分布,也会有一部分用户路由不到正确的session - 改进版: 一致性hash算法/带虚拟节点的一致性hash算法
- 优点
-
方案四: 统一存储
- 统一存储之使用SpringSession实现
-
pom.xml导入依赖
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
-
application.yml配置
spring: redis: host: 192.168.145.8 port: 6379 # 使用redis存储session解决分布式共享问题 session: store-type: redis server: # session过期时间为30分钟 servlet: session: timeout: 30m
-
主启动类配置@EnableRedisHttpSession注解
package com.kenai.gulimall.auth; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; @EnableRedisHttpSession // 整合redis作为session存储 @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class GulimallAuthServerApplication { public static void main(String[] args) { SpringApplication.run(GulimallAuthServerApplication.class, args); } }
-
session存入redis代码编写
@Controller public class test { // session存入redis @ResponseBody @GetMapping("/test") public String test(HttpSession session){ session.setAttribute("test_session", "data"); return "ok"; } // 从redis中获取session @ResponseBody @GetMapping("/getRedisSession") public String testGet(HttpSession session){ String data = (String) session.getAttribute("test_session"); return data; } }
-
修改域名及session的redis缓存序列化
package com.kenai.gulimall.auth.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.session.web.http.CookieSerializer; import org.springframework.session.web.http.DefaultCookieSerializer; @Configuration public class GulimallSessionConfig { // 改变域名等信息。子域名向redis缓存中存放session信息,为了让整个域名都可用,通过修改域名为根域名实现 @Bean public CookieSerializer cookieSerializer(){ DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer(); cookieSerializer.setDomainName("gulimall.com"); cookieSerializer.setCookieName("GULIMALLSESSION"); return cookieSerializer; } // session存入redis缓存的json序列化 @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer(){ return new GenericJackson2JsonRedisSerializer(); } }