前言:

本项目是基于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;
} @Override
public 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();
} @Override
public void load() throws ClassNotFoundException, IOException { } @Override
public void unload() throws IOException { } /**
* Add a lifecycle event listener to this component.
*
* @param listener
* The listener to add
*/
@Override
public 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.
*/
@Override
public LifecycleListener[] findLifecycleListeners() {
return lifecycle.findLifecycleListeners();
} /**
* Remove a lifecycle event listener from this component.
*
* @param listener
* The listener to remove
*/
@Override
public 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
*/
@Override
protected 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
*/
@Override
protected 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();
} @Override
public 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) {
// 重新生成一个sessionId
sessionId = 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;
} @Override
public Session createEmptySession() {
System.out.println("添加空的session");
return new RedisSession(this);
} @Override
public void add(Session session) {
System.out.println("添加session到redis数据库");
try {
save(session);
} catch (IOException e) { throw new RuntimeException("保存session失败", e);
}
} @Override
public 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();
}
}
} @Override
public void remove(Session session) {
remove(session, false);
} @Override
public 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();
}
} @Override
public 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;
} // 产生一个请求后
@Override
public 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!");
// 删除内存中的session
manager.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二次开发)的更多相关文章

  1. linux下实现redis共享session的tomcat集群

    为了实现主域名与子域名的下不同的产品间一次登录,到处访问的效果,因此采用rediss实现tomcat的集群效果.基于redis能够异步讲缓存内容固化到磁盘上,从而当服务器意外重启后,仍然能够让sess ...

  2. redis集群与分片(1)-redis服务器集群、客户端分片

    下面是来自知乎大神的一段说明,个人觉得非常清晰,就收藏了. 为什么集群? 通常,为了提高网站响应速度,总是把热点数据保存在内存中而不是直接从后端数据库中读取.Redis是一个很好的Cache工具.大型 ...

  3. Redis 5.0.7 讲解,单机、集群模式搭建

    Redis 5.0.7 讲解,单机.集群模式搭建 一.Redis 介绍 不管你是从事 Python.Java.Go.PHP.Ruby等等... Redis都应该是一个比较熟悉的中间件.而大部分经常写业 ...

  4. Redis存储Tomcat集群的Session

    Redis存储Tomcat集群的Session 如何 做到把新开发的代码推送到到生产系统中部署,生产系统要能够零宕机.对使用用户零影响. 设想 是使用集群来搞定,通过通知负载均衡Nginx,取下集群中 ...

  5. 【原创】搭建Nginx(负载均衡)+Redis(Session共享)+Tomcat集群

    为什么移除首页?哪里不符合要求?倒是回我邮件啊! 一.环境搭建 Linux下Vagrant搭建Tomcat7.Java7 二.Nginx的安装配置与测试 *虚拟机下转至root sudo -i 1)下 ...

  6. 搭建Nginx(负载均衡)+Redis(Session共享)+Tomcat集群

    一.环境搭建 Linux下Vagrant搭建Tomcat7.Java7 二.Nginx的安装配置与测试 *虚拟机下转至root sudo -i 1)下载并解压(目前官网最新版本) 创建安装目录:mkd ...

  7. Tomcat集群+Nginx+Redis服务搭建

    由于公司新业务突然上来了,单个Tomcat实例已经不能满足业务发展的需要了,只能通过搭建集群来解决问题了.所以就出现了下面的内容: 1.Redis保存Session信息 为了保存Session信息在集 ...

  8. 利用Redis发布订阅完成tomcat集群下的消息通知

    以下为个人想法,如果有说的不对的地方请各位大佬见谅! 这是博主的第一篇博客,可能排版以及一些描述有不合理的地方还请勿喷,希望大家尽可能的多给我这样的新人一些鼓励让我能在写博客的道路上走下去. 进入正题 ...

  9. redis集群与分片(2)-Redis Cluster集群的搭建与实践

    Redis Cluster集群 一.redis-cluster设计 Redis集群搭建的方式有多种,例如使用zookeeper等,但从redis 3.0之后版本支持redis-cluster集群,Re ...

  10. 基于tomcat集群做session共享

    前端代理服务器nginx:192.168.223.136 tomcat服务器:采用的一台多实例192.168.223.146:8081,192.168.223.146:8082(如何构建多实例tomc ...

随机推荐

  1. C#网络程序设计(1)网络编程常识与C#常用特性

        网络程序设计能够帮我们了解联网应用的底层通信原理!     (1)网络编程常识: 1)什么是网络编程 只有主要实现进程(线程)相互通信和基本的网络应用原理性(协议)功能的程序,才能算是真正的网 ...

  2. Asp.net core WebApi 使用Swagger生成帮助页

    最近我们团队一直进行.net core的转型,web开发向着前后端分离的技术架构演进,我们后台主要是采用了asp.net core webapi来进行开发,开始每次调试以及与前端人员的沟通上都存在这效 ...

  3. extjs4 前台导出grid数据 生成excel,数据量大后台无法接收到数据

    最近做的一个web项目使用的是extsj4 框架,需要一个导出excel功能,通过extjs4 自带的导出方法实现.在前台生成excel的代码,form提交传递到后台输出.前台grid数据超过1000 ...

  4. Linux环境下用户空间与内核空间数据的交换方式

    在linux环境开发过程中,经常会需要在用户空间和内核空间之间进行数据交换. 介绍了 Linux 系统下用户空间与内核空间数据交换的几种方式 第一节:使用procfs实现内核交互简明教程(1) 第二节 ...

  5. CentOS 7安装Docker

    在虚拟机CentOS 7上安装Docker   ## 零:检查前提条件:   在Red Hat 和Red Hat系列的Linux发行版中,安装Docker所需的前提提交并不多.     ### 1.内 ...

  6. 略过 Mysql 5.7的密码策略

    之前从mysql 5.6的时候,mysql 还没有密码策略这个东东,所以我们每个用户的密码都可以随心所欲地设置,什么123 ,abc 这些,甚至你搞个空格,那也是OK的. 而mysql.user 表里 ...

  7. 一句话告诉你JQuery $(this)到底指的是什么,怎么用

    看了网上好多关于jquery $(this)的解释,感觉都说的很模糊. 下面说出我自己的理解. this表示的是当前对象,下面以例子来说明 <!DOCTYPE html> <html ...

  8. maven(03)

    修改本地库路径 windows下maven默认路径应该是${user.home}/.m2/repository 修改方法:找到maven安装的根路径,里面有一个conf的文件夹,打开里面有一个sett ...

  9. JAVA CyclicBarrier类详解

    一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrie ...

  10. VR的技术问题是不是市场的绊脚石?

    VR虽然现在很火,但是不得不说,VR虚拟现实设备现在还没有普及,而且虚拟现实设备要想像手机一样普及,还面临着很多的困难和挑战.当然最重要的是,VR虚拟现实设备要解决一些问题才可以,这些问题也是影响VR ...