基于redis实现tomcat8及以上版本的tomcat集群的session持久化实现(tomcat-redis-session-manager二次开发)

来源:互联网 发布:c语言比较字符串大小 编辑:程序博客网 时间:2024/05/18 03:45

前言:

本项目是基于jcoleman的tomcat-redis-session-manager二次开发版本

1、修改了小部分实现逻辑

2、去除对juni.jar包的依赖

3、去除无效代码和老版本tomcat操作API

4、支持tomcat 8 及以后的版本

感谢jcoleman的项目: https://github.com/jcoleman/tomcat-redis-session-manager,由于该项目已经停止更新,最新版本只支持tomcat7,对于tomcat7以后的版本都不支持。

源码提供:

github项目地址:https://github.com/eguid/tomcat-redis-sessioon-manager

下载目录:

tomcat-redis-session-manager-by-eguid.jar下载地址:http://download.csdn.net/detail/eguid_1/9638171

tomcat-redis-session-manager-by-eguid.jar+jedis-2.9.0.jar+commons-pool2-2.2.jar集合包下载


注意:本项目依赖5个jar包,tomcat-api.jar;catalina.jar;servlet-api.jar;jedis-2.9.0.jar;commons-pool-2.4.2.jar,其中tomcat-api.jar、catalina.jar和servlet-api.jar这三个包是tomcat原生jar包,本项目打包时不需要打入这三个包


一、主要代码实现

1、session管理器实现

该类用于实现session的基本增删改查操作,加入了redis实现持久化

package cn.eguid.redisSessionManager;import org.apache.catalina.Lifecycle;import org.apache.catalina.LifecycleException;import org.apache.catalina.LifecycleListener;import org.apache.catalina.util.LifecycleSupport;import org.apache.catalina.LifecycleState;import org.apache.catalina.Valve;import org.apache.catalina.Session;import org.apache.catalina.session.ManagerBase;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;import redis.clients.jedis.Jedis;import redis.clients.jedis.Protocol;import java.io.IOException;import java.util.Arrays;import java.util.Set;/** *  * @author eguid *  */public class RedisSessionManager extends ManagerBase implements Lifecycle {protected byte[] NULL_SESSION = "null".getBytes();protected String host = "localhost";protected int port = 6379;protected int database = 0;protected String password = null;protected int timeout = Protocol.DEFAULT_TIMEOUT;protected JedisPool connectionPool = null;protected RedisSessionHandlerValve handlerValve;protected ThreadLocal<RedisSession> currentSession = new ThreadLocal<RedisSession>();protected ThreadLocal<String> currentSessionId = new ThreadLocal<String>();protected ThreadLocal<Boolean> currentSessionIsPersisted = new ThreadLocal<Boolean>();protected Serializer serializer;protected static String name = "RedisSessionManager";// 用于序列化的类protected String serializationStrategyClass = "cn.eguid.redisSessionManager.JavaSerializer";protected LifecycleSupport lifecycle = new LifecycleSupport(this);public String getHost() {return host;}public void setHost(String host) {this.host = host;}public int getPort() {return port;}public void setPort(int port) {this.port = port;}public int getDatabase() {return database;}public void setDatabase(int database) {this.database = database;}public int getTimeout() {return timeout;}public void setTimeout(int timeout) {this.timeout = timeout;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public void setSerializationStrategyClass(String strategy) {this.serializationStrategyClass = strategy;}@Overridepublic int getRejectedSessions() {// Essentially do nothing.return 0;}public void setRejectedSessions(int i) {// Do nothing.}protected Jedis getConnection() {System.out.println("获取jedis连接");Jedis jedis = connectionPool.getResource();if (getDatabase() != 0) {jedis.select(getDatabase());}return jedis;}protected void returnConnection(Jedis jedis) {System.out.println("注销jedis连接");jedis.close();}@Overridepublic void load() throws ClassNotFoundException, IOException {}@Overridepublic void unload() throws IOException {}/** * Add a lifecycle event listener to this component. * * @param listener *            The listener to add */@Overridepublic void addLifecycleListener(LifecycleListener listener) {lifecycle.addLifecycleListener(listener);}/** * Get the lifecycle listeners associated with this lifecycle. If this * Lifecycle has no listeners registered, a zero-length array is returned. */@Overridepublic LifecycleListener[] findLifecycleListeners() {return lifecycle.findLifecycleListeners();}/** * Remove a lifecycle event listener from this component. * * @param listener *            The listener to remove */@Overridepublic void removeLifecycleListener(LifecycleListener listener) {lifecycle.removeLifecycleListener(listener);}/** * Start this component and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException *                if this component detects a fatal error that prevents this *                component from being used */@Overrideprotected synchronized void startInternal() throws LifecycleException {boolean isSucc=false;try {System.out.println("准备开启redis-session-Manager处理器 ... ");super.startInternal();setState(LifecycleState.STARTING);Boolean attachedToValve = false;Valve[] values = getContainer().getPipeline().getValves();for (Valve valve : values) {if (valve instanceof RedisSessionHandlerValve) {System.out.println("初始化redis-session-Manager处理器 ... ");this.handlerValve = (RedisSessionHandlerValve) valve;this.handlerValve.setRedisSessionManager(this);attachedToValve = true;break;}}if (!attachedToValve) {String error = "重大错误:redis-session-Manager无法添加到会话处理器,session在请求后不能正常启动处理器!";throw new LifecycleException(error);}System.out.println("初始化序列化器和反序列化器 ... ");initializeSerializer();initializeDatabaseConnection();setDistributable(true);isSucc=true;} catch (ClassNotFoundException e) {throw new LifecycleException(e);} catch (InstantiationException e) {throw new LifecycleException(e);} catch (IllegalAccessException e) {throw new LifecycleException(e);} catch(Exception e){throw e;}finally{if(isSucc){System.out.println("redis-session-manager初始化成功");}else{System.out.println("redis-session-manager初始化失败");}}}/** * Stop this component and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException *                if this component detects a fatal error that prevents this *                component from being used */@Overrideprotected synchronized void stopInternal() throws LifecycleException {System.err.println("停止redis-session-manager处理器!");setState(LifecycleState.STOPPING);try {if (connectionPool != null) {connectionPool.destroy();}} catch (Exception e) {System.err.println("注销redis连接池失败!");connectionPool = null;}super.stopInternal();}@Overridepublic Session createSession(String sessionId) {System.out.println("根据sessionId创建session:" + sessionId);// 初始化设置并创建一个新的session返回RedisSession session = (RedisSession) createEmptySession();session.setNew(true);session.setValid(true);session.setCreationTime(System.currentTimeMillis());session.setMaxInactiveInterval(getMaxInactiveInterval());String jvmRoute = getJvmRoute();Jedis jedis = null;try {jedis = getConnection();do {if (null == sessionId) {// 重新生成一个sessionIdsessionId = generateSessionId();}if (jvmRoute != null) {sessionId += '.' + jvmRoute;}} while (jedis.setnx(sessionId.getBytes(), NULL_SESSION) == 1L);/* * Even though the key is set in Redis, we are not going to flag the * current thread as having had the session persisted since the * session isn't actually serialized to Redis yet. This ensures that * the save(session) at the end of the request will serialize the * session into Redis with 'set' instead of 'setnx'. */session.setId(sessionId);session.tellNew();currentSession.set(session);currentSessionId.set(sessionId);currentSessionIsPersisted.set(false);} finally {if (jedis != null) {jedis.close();}}return session;}@Overridepublic Session createEmptySession() {System.out.println("添加空的session");return new RedisSession(this);}@Overridepublic void add(Session session) {System.out.println("添加session到redis数据库");try {save(session);} catch (IOException e) {throw new RuntimeException("保存session失败", e);}}@Overridepublic Session findSession(String id) throws IOException {System.out.println("查找sessionId:" + id);RedisSession session = null;if (id == null) {session = null;currentSessionIsPersisted.set(false);} else if (id.equals(currentSessionId.get())) {session = currentSession.get();} else {session = loadSessionFromRedis(id);if (session != null) {currentSessionIsPersisted.set(true);}}currentSession.set(session);currentSessionId.set(id);return session;}public void clear() {Jedis jedis = null;try {jedis = getConnection();jedis.flushDB();} finally {if (jedis != null) {jedis.close();}}}public int getSize() throws IOException {Jedis jedis = null;try {jedis = getConnection();int size = jedis.dbSize().intValue();return size;} finally {if (jedis != null) {jedis.close();}}}public String[] keys() throws IOException {Jedis jedis = null;try {jedis = getConnection();Set<String> keySet = jedis.keys("*");return keySet.toArray(new String[keySet.size()]);} finally {if (jedis != null) {jedis.close();}}}public RedisSession loadSessionFromRedis(String id) throws IOException {RedisSession session;Jedis jedis = null;try {jedis = getConnection();byte[] data = jedis.get(id.getBytes());if (data == null) {session = null;} else if (Arrays.equals(NULL_SESSION, data)) {throw new IllegalStateException("Race condition encountered: attempted to load session[" + id+ "] which has been created but not yet serialized.");} else {session = (RedisSession) createEmptySession();serializer.deserializeInto(data, session);session.setId(id);session.setNew(false);session.setMaxInactiveInterval(getMaxInactiveInterval() * 1000);session.access();session.setValid(true);session.resetDirtyTracking();}return session;} catch (IOException e) {throw e;} catch (ClassNotFoundException ex) {throw new IOException("Unable to deserialize into session", ex);} finally {if (jedis != null) {jedis.close();}}}/** * save session to redis *  * @param session * @throws IOException */public void save(Session session) throws IOException {System.out.println("保存session到redis");Jedis jedis = null;try {RedisSession redisSession = (RedisSession) session;Boolean sessionIsDirty = redisSession.isDirty();redisSession.resetDirtyTracking();byte[] binaryId = redisSession.getId().getBytes();jedis = getConnection();if (sessionIsDirty || currentSessionIsPersisted.get() != true) {jedis.set(binaryId, serializer.serializeFrom(redisSession));}currentSessionIsPersisted.set(true);jedis.expire(binaryId, getMaxInactiveInterval());} catch (IOException e) {throw e;} finally {if (jedis != null) {jedis.close();}}}@Overridepublic void remove(Session session) {remove(session, false);}@Overridepublic void remove(Session session, boolean update) {System.out.println("删除redis中的session,更新:"+update);Jedis jedis = null;try {jedis = getConnection();jedis.del(session.getId());} finally {if (jedis != null) {jedis.close();}}}public void afterRequest() {System.out.println("删除缓存在内存中的session");RedisSession redisSession = currentSession.get();if (redisSession != null) {currentSession.remove();currentSessionId.remove();currentSessionIsPersisted.remove();}}@Overridepublic void processExpires() {// We are going to use Redis's ability to expire keys for session// expiration.// Do nothing.}private void initializeDatabaseConnection() throws LifecycleException {try {System.out.println("初始化redis连接池 ... ");// 初始化redis连接池connectionPool = new JedisPool(new JedisPoolConfig(), getHost(), getPort(), getTimeout(), getPassword());} catch (Exception e) {e.printStackTrace();throw new LifecycleException("redis连接池初始化错误,redis不存在或配置错误!", e);}}private void initializeSerializer() throws InstantiationException, IllegalAccessException, ClassNotFoundException {System.out.println("准备初始化序列器 ... ");serializer = (Serializer) Class.forName(serializationStrategyClass).newInstance();ClassLoader classLoader = null;if (getContainer() != null) {classLoader = getContainer().getClass().getClassLoader();}System.out.println("初始化序列器完成!");serializer.setClassLoader(classLoader);}}

2、redis的session实现

package cn.eguid.redisSessionManager;import java.security.Principal;import org.apache.catalina.Manager;import org.apache.catalina.session.StandardSession;import java.util.HashMap;public class RedisSession extends StandardSession {  protected static Boolean manualDirtyTrackingSupportEnabled = false;  public static void setManualDirtyTrackingSupportEnabled(Boolean enabled) {    manualDirtyTrackingSupportEnabled = enabled;  }  protected static String manualDirtyTrackingAttributeKey = "__changed__";  public static void setManualDirtyTrackingAttributeKey(String key) {    manualDirtyTrackingAttributeKey = key;  }  protected HashMap<String, Object> changedAttributes;  protected Boolean dirty;  public RedisSession(Manager manager) {    super(manager);    resetDirtyTracking();  }  public Boolean isDirty() {    return dirty || !changedAttributes.isEmpty();  }  public HashMap<String, Object> getChangedAttributes() {    return changedAttributes;  }  public void resetDirtyTracking() {    changedAttributes = new HashMap<String, Object>();    dirty = false;  }  @Override  public void setAttribute(String key, Object value) {    if (manualDirtyTrackingSupportEnabled && manualDirtyTrackingAttributeKey.equals(key)) {      dirty = true;      return;    }    Object oldValue = getAttribute(key);    if ( value == null && oldValue != null         || oldValue == null && value != null         || !value.getClass().isInstance(oldValue)         || !value.equals(oldValue) ) {      changedAttributes.put(key, value);    }    super.setAttribute(key, value);  }  @Override  public void removeAttribute(String name) {    dirty = true;    super.removeAttribute(name);  }  @Override  public void setId(String id) {    this.id = id;  }  @Override  public void setPrincipal(Principal principal) {    dirty = true;    super.setPrincipal(principal);  }}

3、session处理器实现

该类可以用于在请求前后请求后做一些操作,不仅局限于session操作,可以做servlet中的所有操作

package cn.eguid.redisSessionManager;import org.apache.catalina.Session;import org.apache.catalina.connector.Request;import org.apache.catalina.connector.Response;import org.apache.catalina.valves.ValveBase;import javax.servlet.ServletException;import java.io.IOException;public class RedisSessionHandlerValve extends ValveBase {// redis-session-manager管理器操作private RedisSessionManager manager;// 通过tomcat的context.xml可以注入该实例public void setRedisSessionManager(RedisSessionManager manager) {this.manager = manager;}// 产生一个请求后@Overridepublic void invoke(Request request, Response response) throws IOException, ServletException {try {getNext().invoke(request, response);} finally {System.out.println("请求完毕后,redis-session-manager正在获取当前产生的session");Session session = request.getSessionInternal(false);storeOrRemoveSession(session);System.out.println("redis-session-manager操作结束,正在清理内存中的session!");// 删除内存中的sessionmanager.afterRequest();}}private void storeOrRemoveSession(Session session) {try {if (session!=null && session.isValid() && session.getSession() != null) {manager.save(session);} else {manager.remove(session);}} catch (Exception e) {System.err.println("提示一下:session操作失败");}}}


二、如何配置该项目到tomcat

1、拷贝tomcat-redis-session-manager-by-eguid.jar,jedis-2.9.0.jar,commons-pool2-2.2.jar到tomcat/lib目录下


2、修改Tomcat context.xml (or the context block of the server.xml if applicable.)

<Valve className="cn.eguid.redisSessionManager.RedisSessionHandlerValve"/>
<Manager className="cn.eguid.redisSessionManager.RedisSessionManager"
         host="192.168.30.21"
         port="6379"
         database="14"
         maxInactiveInterval="1800"/>


1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 百度云有转存任务正在进行怎么办 港版索尼手机的浏览器怎么办 手机下载文件不能直接打开怎么办 上传到电脑视频打不开了怎么办 百度云播放很卡怎么办 百度云视频很卡怎么办 考虫课程过期了怎么办 私教课程过期了怎么办 在异地交的社保怎么办 1岁宝宝吃粉笔怎么办 宝宝吃了彩色笔怎么办 宝宝吃了彩笔水怎么办 熹妃q传忘记密码怎么办 云课堂2忘记密码怎么办 云课堂密码忘了怎么办 小米云存储空间已经满怎么办 苹果手机云备份空间不足怎么办 办信用卡不知道公司电话怎么办 办信用卡不知道单位电话怎么办 朋友沉迷网络游戏怎么办英文作文 平板的密码忘了怎么办 白泥面膜过期了怎么办 小米手机桌面短信图标不见了怎么办 亚马逊账号注册扣款不成功怎么办 流量卡不用了怎么办啊 移动卡不想要了怎么办 b站调用速度过快怎么办 苹果系统占用19g怎么办 id被锁定了忘记密码怎么办 360儿童卫士忘记密码怎么办 微信注册忘记密码怎么办 旅行青蛙换手机iphone怎么办 网页邮箱添加不了附件怎么办 pos机密码忘了怎么办 在4楼上课地震了怎么办 微鲸电视待机打不开怎么办 苹果手机微信听不到语音怎么办 电信怎么办腾讯频看电视不要流量 孩子做事效率低怎么办教案 孩子做事效率低怎么办读后感 做事效率低总是焦虑怎么办