- 分布式环境下的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信息,为了让整个域名都可用,通过修改域名为根域名实现
// gulimall.com域名及子域名的cookie中有一个名叫GULIMALLSESSION的cookie
// GULIMALLSESSION当中存放sessionid信息,当向redis请求的时候会带上cookie信息,所以便能根据cookie中存放的sessionId找到redis中存放的session信息
// session信息中有我们存入的属性信息,比如用户id:用户信息.
@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();
}
}