👉本篇速览
-
session存在的问题
- 分布式session如何解决
- nginx的ip_hash
- 分布式session如何解决
-
spring-session
- 查询的原理&源码
- 过期的原理&源码
- 扩展redis过期策略
- 为何spring-session要如此设计数据结构
-
token取代session,实现服务端到客户端的跨变
🎯session存在的问题
- 服务端需要存储session,占用内存高
- 不同服务器,无法共享session【分布式的场景】,这种情况下通常需要借助redis等数据库来做存储
没有什么是加一层解决不了的hhh
分布式session如何解决
当我们用nginx做负载均衡时,用户在A服务器登录了,A服务器存储了session,客户端也存储了cookie,其中有JSESSIONID。
此时负载均衡,访问B服务器的话,B服务器是没有这个session的,客户端的cookie里边JSESSIONID也就找不到对应的session,相当于没有登录,此时如何解决呢?
nginx的ip_hash
用nginx的ip_hash可以使得某个ip的用户,只固定访问某个特定的服务器,这样就不会跑到其他服务器,也就不需要考虑session共享的问题了
但与此同时,这又违背了Nginx负载均衡的初衷,请求都固定打到某一台服务器,宕机就不好办了,于是我们有了spring-session
🎯spring session
查询的原理
当请求进来的时候,SessionRepositoryFilter 会先拦截到请求,将 request 和 response 对象转换成 SessionRepositoryRequestWrapper 和SessionRepositoryResponseWrapper 。后续当第一次调用 request 的getSession方法时,会调用到 SessionRepositoryRequestWrapper 的getSession
方法。
这个方法是被重写过的,逻辑是先从 request 的属性中查找,如果找不到;再查找一个key值是"SESSION"的 Cookie,通过这个 Cookie 拿到 SessionId 去 Redis 中查找,如果查不到,就直接创建一个RedisSession 对象,同步到 Redis 中。
说的简单点就是:拦截请求,将之前在服务器内存中进行 Session 创建销毁的动作,改成在 Redis 中创建。
具体源码
/**
* HttpServletRequest getSession()实现
*/
@Override
public HttpSessionWrapper getSession() {
return getSession(true);
}
@Override
public HttpSessionWrapper getSession(boolean create) {
HttpSessionWrapper currentSession = getCurrentSession();
if (currentSession != null) {
return currentSession;
}
//从当前请求获取sessionId
String requestedSessionId = getRequestedSessionId();
if (requestedSessionId != null
&& getAttribute(INVALID_SESSION_ID_ATTR) == null) {
S session = getSession(requestedSessionId);
if (session != null) {
this.requestedSessionIdValid = true;
currentSession = new HttpSessionWrapper(session, getServletContext());
currentSession.setNew(false