基于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"/>
- 基于redis实现tomcat8及以上版本的tomcat集群的session持久化实现(tomcat-redis-session-manager二次开发)
- 使用redis实现自己的tomcat session manager(java)
- 基于nginx tomcat-redis-session-manager实现session共享
- 用Redis存储Tomcat集群的Session实现session共享
- 基于Redis的Tomcat集群Session共享
- tomcat集群-基于redis的session共享
- tomcat集群-基于redis的session共享
- Redis3.2+Tomcat实现集群的Session管理 -- tomcat-redis-session-manager的编译和开发部署环境搭建
- Redis3.2+Tomcat实现集群的Session管理 -- tomcat-redis-session-manager的编译和开发部署环境搭建
- Redis3.2+Tomcat实现集群的Session管理 -- tomcat-redis-session-manager的编译和开发部署环境搭建
- tomcat-redis-session-manager实现tomcat session共享
- linux下实现redis共享session的tomcat集群
- 使用Tomcat-redis-session-manager来实现Tomcat集群部署中的Session共享
- redis实现tomcat集群session共享
- redis集群 实现 tomcat session 共享
- tomcat-redis-session-manager的使用
- 使用tomcat-redis-session-manager实现session共享
- Nginx+Tomcat搭建集群,Spring Session+Redis实现Session共享
- selector android
- Maven文件结构分析
- shutil.rmtree 函数 OSError: [Errno 39] Directory not empty: 错误原因
- ORA-12638: 身份证明检索失败
- 第四章 数据的类型
- 基于redis实现tomcat8及以上版本的tomcat集群的session持久化实现(tomcat-redis-session-manager二次开发)
- USB枚举
- soap报文略说
- 初识Android项目知识点积累
- 欢迎使用CSDN-markdown编辑器
- Maven常用命令
- ASP.NET MVC 单点登录(Single Signe On)
- libjpeg-turbo使用实例(编解码jpeg、jpg转bmp、bmp转jpg代码)
- 常用手机uc浏览器兼容问题记录(陆续补充)