一、問題引出
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;
}
}