一、问题引出
1.1 Session的原理
//默认创建一个session,默认值为true没有找到对应的session 自动创建session
HttpSession session = request. getSession();
session.setAttribute("user", nameValue);
Object value = session.getAttribute("user");
- Session分为SessionId和SessionValue,Session本身是一个临时的,sessionid和token(令牌)非常相似保证临时且唯一;
- 请求和响应过程:服务器端接受到客户端请求,会创建一个Session,使用响应头返回SessionId给客户端。浏览器获取到SessionId后,保存在本地Cookie中;
- 下一次请求时:客户端读取到本地的SessionId,存放在请求头中,服务器端从请求头中获取到对应的Sessionid,使用SesisonId在本地Session内存中查询。
1.2 问题概述
1.分布式Session一致性(白话文服务器集群Session共享的问题)
2.分析分布式Session一致性
3.Session的作用?服务器(Tomcat) 与客户端(浏览器)保存整个通讯的会话基本信息。
4.应用场景: javaee基础 登陆流程做法(账号密码登陆成功之后,获取到userid,存放在session,下次获取用户信息的之后,直接从session会话中获取。)防止表单重复提交。
Session理解本地jvm缓存,sesison存放 服务器,返回sessionid给客户端。
二、解决方案
- 使用nginx (反向代理) ip绑定 同一个ip 只能在指定的同一个机器访问(没有 负载均衡)
- 使用数据库效率不是很高
- tomcat内置支持对session同步( 不推荐),同步可能会产生延迟
- 使用SpringSession框架相当于把我们的Session值缓存到redis中。面试题:你们项目在发布的时候,Session如何控制不失效的? 使用缓存框架,缓存Session的值(一级和二级) Spring Session重写httpsession框架,将对应的值缓存到redis中有点类似与一级和二级缓存
- 以使用token替代Session功能, sessionid不同他的域名也不同,移动会话信息使用令牌方式替代Session,Token最终存放在redis中, redis支持分布式共享
三、代码实现-使用Token代替Session
Token存放在Redis中
3.1 Service
RedisService.java
@Component
public class RedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
// public void set(String key, Object object, Long time) {
// stringRedisTemplate.opsForValue();
// // 存放String 类型
// if (object instanceof String) {
// setString(key, object);
// }
// // 存放 set类型
// if (object instanceof Set) {
// setSet(key, object);
// }
// // 设置有效期 以秒为单位
// stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
// }
//
public void setString(String key, Object object) {
// 开启事务权限
// stringRedisTemplate.setEnableTransactionSupport(true);
try {
// 开启事务 begin
// stringRedisTemplate.multi();
String value = (String) object;
stringRedisTemplate.opsForValue().set(key, value);
System.out.println("存入完毕,马上开始提交redis事务");
// 提交事务
// stringRedisTemplate.exec();
} catch (Exception e) {
// 需要回滚事务
// stringRedisTemplate.discard();
}
}
public void setSet(String key, Object object) {
Set<String> value = (Set<String>) object;
for (String oj : value) {
stringRedisTemplate.opsForSet().add(key, oj);
}
}
public String getString(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
}
TokenService.java
@Service
public class TokenService {
@Autowired
private RedisService redisService;
// 新增 返回token
public String put(String value) {
//1.判断是否为空
if(value == null) {
return null;
}
//2. 获取对应的token(token实际等于key)
String token = getToken();
//3.存入redis中
redisService.setString(token, value);
//4.返回token
return token;
}
// 获取信息
public String get(String token) {
String reuslt = redisService.getString(token);
return reuslt;
}
public String getToken() {
return UUID.randomUUID().toString();
}
}
3.2 TokenController
@RestController
public class TokenController {
@Autowired
private TokenService tokenService;
@Value("${server.port}")
private String serverPort;
@RequestMapping("/put")
public String put(String nameValue) {
String token = tokenService.put(nameValue);
return token + "-" + serverPort;
}
@RequestMapping("/get")
public String get(String token) {
String value = tokenService.get(token);
return value + "-" + serverPort;
}
}