想要作Tomcat集群,其中须要解决的一个问题就是多个Tomcat中session的共享。共享的方法有不少种,好比使用Tomcat自带的session复制,使用数据库等。这里一些介绍我使用过的方法。java
这种方法实现起来比较容易,可是须要改动每一个Tomcat服务器的配置。对于Tomcat6和7,能够使用tomcat-redis-session-manager库来实现,对于Tomcat8以及8以上的版本,能够搜索对应的库。git
tomcat-redis-session-manager(Github):https://github.com/jcoleman/tomcat-redis-session-managergithub
库的介绍里有详细的使用方法,这里再简单列一下:web
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" /> <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" host="localhost" <!-- 非必填: 默认值 "localhost" --> port="6379" <!-- 非必填: 默认值 "6379" --> database="0" <!-- 非必填: 默认值 "0" --> maxInactiveInterval="60" <!-- 非必填: 默认值 "60" (秒) --> sessionPersistPolicies="PERSIST_POLICY_1,PERSIST_POLICY_2,.." <!-- 非必填 --> sentinelMaster="SentinelMasterName" <!-- 非必填 --> sentinels="sentinel-host-1:port,sentinel-host-2:port,.." <!-- 非必填 --> />
注意:Value标签必须在Manager标签以前redis
重启Tomcat后生效。数据库
若是项目中使用了Spring和Apache Shiro,能够使用下面的方法。apache
序列化工具示例:tomcat
import java.io.Serializable; import org.apache.commons.lang3.SerializationUtils; import org.apache.shiro.codec.Base64; import org.apache.shiro.session.Session; public class ShiroSerializationUtils { public static String serialize(Session session) { return Base64.encodeToString(SerializationUtils.serialize((Serializable) session)); } public static Session deserialize(String sessionStr) { return SerializationUtils.deserialize(Base64.decode(sessionStr)); } }
Redis操做类示例:服务器
import java.util.Set; import javax.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class RedisManager { private String url = null; private int port = 6379; private int timeout = 0; private String password = null; private JedisPool jedisPool = null; public RedisManager(){ } @PostConstruct public void init() { url = StringUtils.defaultIfBlank(url, "127.0.0.1"); if(StringUtils.isNotBlank(password)) { jedisPool = new JedisPool(new JedisPoolConfig(), url, port, timeout, password); } else if (timeout != 0) { jedisPool = new JedisPool(new JedisPoolConfig(), url, port, timeout); } else { jedisPool = new JedisPool(new JedisPoolConfig(), url, port); } } public Jedis getJedis() { return jedisPool.getResource(); } public String get(String key){ try(Jedis jedis = jedisPool.getResource();){ return jedis.get(key); } } public void set(String key, String value){ try(Jedis jedis = jedisPool.getResource();){ jedis.set(key, value); } } public void set(String key, String value, int timeToLiveSeconds){ try(Jedis jedis = jedisPool.getResource();){ jedis.setex(key, timeToLiveSeconds, value); } } public void del(String key){ try(Jedis jedis = jedisPool.getResource();){ jedis.del(key); } } public Set<String> keys(String pattern){ try(Jedis jedis = jedisPool.getResource();){ return jedis.keys(pattern); } } public void setUrl(String url) { this.url = url; } public void setPort(int port) { this.port = port; } public void setTimeout(int timeout) { this.timeout = timeout; } public void setPassword(String password) { this.password = password; } }
实现AbstractSessionDAO示例:session
import java.io.Serializable; import java.util.Collection; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.ValidatingSession; import org.apache.shiro.session.mgt.eis.AbstractSessionDAO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RedisSessionDao extends AbstractSessionDAO { private static final Logger log = LoggerFactory.getLogger(RedisSessionDao.class); private int expirationTime = 1800; // 超时时间,秒 private RedisManager redisManager; @Override protected Serializable doCreate(Session session) { log.debug("Create session: '{}'",session.getId()); Serializable sessionId = this.generateSessionId(session); assignSessionId(session, sessionId); String value = ShiroSerializationUtils.serialize(session); redisManager.set(String.valueOf(sessionId), value, expirationTime); return sessionId; } @Override public void update(Session session) throws UnknownSessionException { log.debug("update session: '{}'",session.getId()); if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()) { return; } redisManager.set(String.valueOf(session.getId()),ShiroSerializationUtils.serialize(session), expirationTime); } @Override public void delete(Session session) { log.debug("delete session: '{}'",session.getId()); redisManager.del(String.valueOf(session.getId())); } @Override protected Session doReadSession(Serializable sessionId) { log.debug("Read session: '{}'",sessionId); String sessionStr = redisManager.get(String.valueOf(sessionId)); return sessionStr == null ? null : ShiroSerializationUtils.deserialize(sessionStr); } //使用 会话验证调度器 需实现此方法 @Override public Collection<Session> getActiveSessions() { return null; } public void setExpirationTime(int expirationTime) { this.expirationTime = expirationTime; } public void setRedisManager(RedisManager redisManager) { this.redisManager = redisManager; } }
Spring对应部分配置示例:
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <property name="sessionDAO" ref="redisSessionDao"/> </bean> <bean id="redisManager" class="RedisManager" > <property name="url" value="${redis.url}" /> <property name="password" value="${redis.password}"></property> </bean> <bean id="redisSessionDao" class="RedisSessionDao"> <property name="redisManager" ref="redisManager" /> <property name="expirationTime" value="442000" /><!-- 秒为单位 --> </bean>