想要作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>