分布式session
单服务器web应用中,session信息只需存在该服务器中,随着分布式系统的流行,单系统已经不能满足日益增长的百万级用户的需求,集群方式部署服务器已在很多公司运用起来,当高并发量的请求到达服务端的时候通过负载均衡的方式分发到集群中的某个服务器,这样就有可能导致同一个用户的多次请求被分发到集群的不同服务器上,就会出现取不到session数据的情况,于是出现了分布式Session。
将session存储在一个中间件 例如存储在 redis中,这样解决了由于服务器不同而取不到session值的问题。
Shiro-Redis-Session:
SimpleSession : Session类型
SessionFactory : 直属SessionManager,默认为SimpleSessionFactory ,负责创建Session对象,默认为SimpleSession
SessionDAO : 直属SessionManager,负责将每个Session对象存储起来,需要定制这个组件,以集成redis
SessionManager : 直属SecurityManager,负责管理Session处理的核心调度过程。
定义SessionDAO:
// 定义SessionDAO,负责存储Session对象
public class MySessionDAO extends AbstractSessionDAO {
private RedisTemplate template;//需要注入RedisTemplate,以支持后续的Redis通信
public RedisTemplate getTemplate() {
return template;
}
public void setTemplate(RedisTemplate template) {
this.template = template;
}
//将Session对象存入redis;(由SimpleSessionFactory创建的Session对象)
protected Serializable doCreate(Session session) {
// 生成sessionID
Serializable sessionId = generateSessionId(session);
//存入redis
System.out.println("save session to redis");
assignSessionId(session,sessionId);// 将sessionID赋值给Session对象
// key = 前缀+sessionID ; value = session对象
template.opsForValue().set("session"+sessionId,session);//将Session对象存入Redis,存入时定义了"前缀"
return sessionId;
}
//从redis获取session对象,供项目使用
@Override
protected Session doReadSession(Serializable sessionId) {
// 从redis获取session,获取时,也要定义 “前缀”; [前缀+sessionID]
Session session = (Session)template.opsForValue().get("session"+sessionId);
return session;
}
//session中数据变动时,要将session对象同步到redis
@Override
public void update(Session session) throws UnknownSessionException {
System.out.println("update session");
Serializable sessionId = session.getId();
// 将session,覆盖存储到Redis
template.opsForValue().set("session"+sessionId,session);
}
//session过期时,从redis中删除session
@Override
public void delete(Session session) {
Serializable sessionId = session.getId();//获取sessionID
// 从redis删除session
template.delete("session"+sessionId);
}
//session检测时,需要获取所有session
@Override
public Collection<Session> getActiveSessions() {
//从redis获取全部或部分session
Set keys = template.keys("session*");//通过前缀获取Session
List<Session> list = template.opsForValue().multiGet(keys);
return list;
}
}
配置SessionDAO:
将SessionDAO注册在SessionManager中;
SessionManager注册在SecurityManager中;
注意RedisTemplate的序列化方式。
<!-- SessionManager,使用Shiro的DefaultWebSessionManager即可,其中注入自定义的SessionDAO -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 注册SessionDAO,负责将Session对象存入redis -->
<property name="sessionDAO">
<bean class="com.zhj.dao.MySessionDAO">
<property name="template" ref="redisTemplate2"/>
</bean>
</property>
</bean>
<!-- 注意:此RedisTemplate key序列化采用String序列化,
value没有使用Jackson或FastJson序列化,而是采用的默认的Jdk的序列化 -->
<bean id="ss" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
<bean id="redisTemplate2" class="org.springframework.data.redis.core.RedisTemplate"
p:connectionFactory-ref="jedisConnectionFactory"
p:keySerializer-ref="ss"/>
<!-- Securitymanager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
<!-- 注册cacheManager -->
<property name="cacheManager" ref="cacheManager03"/>
<!-- 注册sessionManager,其中会调度SessionDAO,将session存入Redis -->
<property name="sessionManager" ref="sessionManager"/>
</bean>