前言:

本项目是基于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实现持久化

  1. package cn.eguid.redisSessionManager;
  2. import org.apache.catalina.Lifecycle;
  3. import org.apache.catalina.LifecycleException;
  4. import org.apache.catalina.LifecycleListener;
  5. import org.apache.catalina.util.LifecycleSupport;
  6. import org.apache.catalina.LifecycleState;
  7. import org.apache.catalina.Valve;
  8. import org.apache.catalina.Session;
  9. import org.apache.catalina.session.ManagerBase;
  10. import redis.clients.jedis.JedisPool;
  11. import redis.clients.jedis.JedisPoolConfig;
  12. import redis.clients.jedis.Jedis;
  13. import redis.clients.jedis.Protocol;
  14. import java.io.IOException;
  15. import java.util.Arrays;
  16. import java.util.Set;
  17. /**
  18. *
  19. * @author eguid
  20. *
  21. */
  22. public class RedisSessionManager extends ManagerBase implements Lifecycle {
  23. protected byte[] NULL_SESSION = "null".getBytes();
  24. protected String host = "localhost";
  25. protected int port = 6379;
  26. protected int database = 0;
  27. protected String password = null;
  28. protected int timeout = Protocol.DEFAULT_TIMEOUT;
  29. protected JedisPool connectionPool = null;
  30. protected RedisSessionHandlerValve handlerValve;
  31. protected ThreadLocal<RedisSession> currentSession = new ThreadLocal<RedisSession>();
  32. protected ThreadLocal<String> currentSessionId = new ThreadLocal<String>();
  33. protected ThreadLocal<Boolean> currentSessionIsPersisted = new ThreadLocal<Boolean>();
  34. protected Serializer serializer;
  35. protected static String name = "RedisSessionManager";
  36. // 用于序列化的类
  37. protected String serializationStrategyClass = "cn.eguid.redisSessionManager.JavaSerializer";
  38. protected LifecycleSupport lifecycle = new LifecycleSupport(this);
  39. public String getHost() {
  40. return host;
  41. }
  42. public void setHost(String host) {
  43. this.host = host;
  44. }
  45. public int getPort() {
  46. return port;
  47. }
  48. public void setPort(int port) {
  49. this.port = port;
  50. }
  51. public int getDatabase() {
  52. return database;
  53. }
  54. public void setDatabase(int database) {
  55. this.database = database;
  56. }
  57. public int getTimeout() {
  58. return timeout;
  59. }
  60. public void setTimeout(int timeout) {
  61. this.timeout = timeout;
  62. }
  63. public String getPassword() {
  64. return password;
  65. }
  66. public void setPassword(String password) {
  67. this.password = password;
  68. }
  69. public void setSerializationStrategyClass(String strategy) {
  70. this.serializationStrategyClass = strategy;
  71. }
  72. @Override
  73. public int getRejectedSessions() {
  74. // Essentially do nothing.
  75. return 0;
  76. }
  77. public void setRejectedSessions(int i) {
  78. // Do nothing.
  79. }
  80. protected Jedis getConnection() {
  81. System.out.println("获取jedis连接");
  82. Jedis jedis = connectionPool.getResource();
  83. if (getDatabase() != 0) {
  84. jedis.select(getDatabase());
  85. }
  86. return jedis;
  87. }
  88. protected void returnConnection(Jedis jedis) {
  89. System.out.println("注销jedis连接");
  90. jedis.close();
  91. }
  92. @Override
  93. public void load() throws ClassNotFoundException, IOException {
  94. }
  95. @Override
  96. public void unload() throws IOException {
  97. }
  98. /**
  99. * Add a lifecycle event listener to this component.
  100. *
  101. * @param listener
  102. * The listener to add
  103. */
  104. @Override
  105. public void addLifecycleListener(LifecycleListener listener) {
  106. lifecycle.addLifecycleListener(listener);
  107. }
  108. /**
  109. * Get the lifecycle listeners associated with this lifecycle. If this
  110. * Lifecycle has no listeners registered, a zero-length array is returned.
  111. */
  112. @Override
  113. public LifecycleListener[] findLifecycleListeners() {
  114. return lifecycle.findLifecycleListeners();
  115. }
  116. /**
  117. * Remove a lifecycle event listener from this component.
  118. *
  119. * @param listener
  120. * The listener to remove
  121. */
  122. @Override
  123. public void removeLifecycleListener(LifecycleListener listener) {
  124. lifecycle.removeLifecycleListener(listener);
  125. }
  126. /**
  127. * Start this component and implement the requirements of
  128. * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
  129. *
  130. * @exception LifecycleException
  131. * if this component detects a fatal error that prevents this
  132. * component from being used
  133. */
  134. @Override
  135. protected synchronized void startInternal() throws LifecycleException {
  136. boolean isSucc=false;
  137. try {
  138. System.out.println("准备开启redis-session-Manager处理器 ... ");
  139. super.startInternal();
  140. setState(LifecycleState.STARTING);
  141. Boolean attachedToValve = false;
  142. Valve[] values = getContainer().getPipeline().getValves();
  143. for (Valve valve : values) {
  144. if (valve instanceof RedisSessionHandlerValve) {
  145. System.out.println("初始化redis-session-Manager处理器 ... ");
  146. this.handlerValve = (RedisSessionHandlerValve) valve;
  147. this.handlerValve.setRedisSessionManager(this);
  148. attachedToValve = true;
  149. break;
  150. }
  151. }
  152. if (!attachedToValve) {
  153. String error = "重大错误:redis-session-Manager无法添加到会话处理器,session在请求后不能正常启动处理器!";
  154. throw new LifecycleException(error);
  155. }
  156. System.out.println("初始化序列化器和反序列化器 ... ");
  157. initializeSerializer();
  158. initializeDatabaseConnection();
  159. setDistributable(true);
  160. isSucc=true;
  161. } catch (ClassNotFoundException e) {
  162. throw new LifecycleException(e);
  163. } catch (InstantiationException e) {
  164. throw new LifecycleException(e);
  165. } catch (IllegalAccessException e) {
  166. throw new LifecycleException(e);
  167. } catch(Exception e){
  168. throw e;
  169. }finally{
  170. if(isSucc){
  171. System.out.println("redis-session-manager初始化成功");
  172. }else{
  173. System.out.println("redis-session-manager初始化失败");
  174. }
  175. }
  176. }
  177. /**
  178. * Stop this component and implement the requirements of
  179. * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
  180. *
  181. * @exception LifecycleException
  182. * if this component detects a fatal error that prevents this
  183. * component from being used
  184. */
  185. @Override
  186. protected synchronized void stopInternal() throws LifecycleException {
  187. System.err.println("停止redis-session-manager处理器!");
  188. setState(LifecycleState.STOPPING);
  189. try {
  190. if (connectionPool != null) {
  191. connectionPool.destroy();
  192. }
  193. } catch (Exception e) {
  194. System.err.println("注销redis连接池失败!");
  195. connectionPool = null;
  196. }
  197. super.stopInternal();
  198. }
  199. @Override
  200. public Session createSession(String sessionId) {
  201. System.out.println("根据sessionId创建session:" + sessionId);
  202. // 初始化设置并创建一个新的session返回
  203. RedisSession session = (RedisSession) createEmptySession();
  204. session.setNew(true);
  205. session.setValid(true);
  206. session.setCreationTime(System.currentTimeMillis());
  207. session.setMaxInactiveInterval(getMaxInactiveInterval());
  208. String jvmRoute = getJvmRoute();
  209. Jedis jedis = null;
  210. try {
  211. jedis = getConnection();
  212. do {
  213. if (null == sessionId) {
  214. // 重新生成一个sessionId
  215. sessionId = generateSessionId();
  216. }
  217. if (jvmRoute != null) {
  218. sessionId += '.' + jvmRoute;
  219. }
  220. } while (jedis.setnx(sessionId.getBytes(), NULL_SESSION) == 1L);
  221. /*
  222. * Even though the key is set in Redis, we are not going to flag the
  223. * current thread as having had the session persisted since the
  224. * session isn't actually serialized to Redis yet. This ensures that
  225. * the save(session) at the end of the request will serialize the
  226. * session into Redis with 'set' instead of 'setnx'.
  227. */
  228. session.setId(sessionId);
  229. session.tellNew();
  230. currentSession.set(session);
  231. currentSessionId.set(sessionId);
  232. currentSessionIsPersisted.set(false);
  233. } finally {
  234. if (jedis != null) {
  235. jedis.close();
  236. }
  237. }
  238. return session;
  239. }
  240. @Override
  241. public Session createEmptySession() {
  242. System.out.println("添加空的session");
  243. return new RedisSession(this);
  244. }
  245. @Override
  246. public void add(Session session) {
  247. System.out.println("添加session到redis数据库");
  248. try {
  249. save(session);
  250. } catch (IOException e) {
  251. throw new RuntimeException("保存session失败", e);
  252. }
  253. }
  254. @Override
  255. public Session findSession(String id) throws IOException {
  256. System.out.println("查找sessionId:" + id);
  257. RedisSession session = null;
  258. if (id == null) {
  259. session = null;
  260. currentSessionIsPersisted.set(false);
  261. } else if (id.equals(currentSessionId.get())) {
  262. session = currentSession.get();
  263. } else {
  264. session = loadSessionFromRedis(id);
  265. if (session != null) {
  266. currentSessionIsPersisted.set(true);
  267. }
  268. }
  269. currentSession.set(session);
  270. currentSessionId.set(id);
  271. return session;
  272. }
  273. public void clear() {
  274. Jedis jedis = null;
  275. try {
  276. jedis = getConnection();
  277. jedis.flushDB();
  278. } finally {
  279. if (jedis != null) {
  280. jedis.close();
  281. }
  282. }
  283. }
  284. public int getSize() throws IOException {
  285. Jedis jedis = null;
  286. try {
  287. jedis = getConnection();
  288. int size = jedis.dbSize().intValue();
  289. return size;
  290. } finally {
  291. if (jedis != null) {
  292. jedis.close();
  293. }
  294. }
  295. }
  296. public String[] keys() throws IOException {
  297. Jedis jedis = null;
  298. try {
  299. jedis = getConnection();
  300. Set<String> keySet = jedis.keys("*");
  301. return keySet.toArray(new String[keySet.size()]);
  302. } finally {
  303. if (jedis != null) {
  304. jedis.close();
  305. }
  306. }
  307. }
  308. public RedisSession loadSessionFromRedis(String id) throws IOException {
  309. RedisSession session;
  310. Jedis jedis = null;
  311. try {
  312. jedis = getConnection();
  313. byte[] data = jedis.get(id.getBytes());
  314. if (data == null) {
  315. session = null;
  316. } else if (Arrays.equals(NULL_SESSION, data)) {
  317. throw new IllegalStateException("Race condition encountered: attempted to load session[" + id
  318. + "] which has been created but not yet serialized.");
  319. } else {
  320. session = (RedisSession) createEmptySession();
  321. serializer.deserializeInto(data, session);
  322. session.setId(id);
  323. session.setNew(false);
  324. session.setMaxInactiveInterval(getMaxInactiveInterval() * 1000);
  325. session.access();
  326. session.setValid(true);
  327. session.resetDirtyTracking();
  328. }
  329. return session;
  330. } catch (IOException e) {
  331. throw e;
  332. } catch (ClassNotFoundException ex) {
  333. throw new IOException("Unable to deserialize into session", ex);
  334. } finally {
  335. if (jedis != null) {
  336. jedis.close();
  337. }
  338. }
  339. }
  340. /**
  341. * save session to redis
  342. *
  343. * @param session
  344. * @throws IOException
  345. */
  346. public void save(Session session) throws IOException {
  347. System.out.println("保存session到redis");
  348. Jedis jedis = null;
  349. try {
  350. RedisSession redisSession = (RedisSession) session;
  351. Boolean sessionIsDirty = redisSession.isDirty();
  352. redisSession.resetDirtyTracking();
  353. byte[] binaryId = redisSession.getId().getBytes();
  354. jedis = getConnection();
  355. if (sessionIsDirty || currentSessionIsPersisted.get() != true) {
  356. jedis.set(binaryId, serializer.serializeFrom(redisSession));
  357. }
  358. currentSessionIsPersisted.set(true);
  359. jedis.expire(binaryId, getMaxInactiveInterval());
  360. } catch (IOException e) {
  361. throw e;
  362. } finally {
  363. if (jedis != null) {
  364. jedis.close();
  365. }
  366. }
  367. }
  368. @Override
  369. public void remove(Session session) {
  370. remove(session, false);
  371. }
  372. @Override
  373. public void remove(Session session, boolean update) {
  374. System.out.println("删除redis中的session,更新:"+update);
  375. Jedis jedis = null;
  376. try {
  377. jedis = getConnection();
  378. jedis.del(session.getId());
  379. } finally {
  380. if (jedis != null) {
  381. jedis.close();
  382. }
  383. }
  384. }
  385. public void afterRequest() {
  386. System.out.println("删除缓存在内存中的session");
  387. RedisSession redisSession = currentSession.get();
  388. if (redisSession != null) {
  389. currentSession.remove();
  390. currentSessionId.remove();
  391. currentSessionIsPersisted.remove();
  392. }
  393. }
  394. @Override
  395. public void processExpires() {
  396. // We are going to use Redis's ability to expire keys for session
  397. // expiration.
  398. // Do nothing.
  399. }
  400. private void initializeDatabaseConnection() throws LifecycleException {
  401. try {
  402. System.out.println("初始化redis连接池 ... ");
  403. // 初始化redis连接池
  404. connectionPool = new JedisPool(new JedisPoolConfig(), getHost(), getPort(), getTimeout(), getPassword());
  405. } catch (Exception e) {
  406. e.printStackTrace();
  407. throw new LifecycleException("redis连接池初始化错误,redis不存在或配置错误!", e);
  408. }
  409. }
  410. private void initializeSerializer() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
  411. System.out.println("准备初始化序列器 ... ");
  412. serializer = (Serializer) Class.forName(serializationStrategyClass).newInstance();
  413. ClassLoader classLoader = null;
  414. if (getContainer() != null) {
  415. classLoader = getContainer().getClass().getClassLoader();
  416. }
  417. System.out.println("初始化序列器完成!");
  418. serializer.setClassLoader(classLoader);
  419. }
  420. }

2、redis的session实现

  1. package cn.eguid.redisSessionManager;
  2. import java.security.Principal;
  3. import org.apache.catalina.Manager;
  4. import org.apache.catalina.session.StandardSession;
  5. import java.util.HashMap;
  6. public class RedisSession extends StandardSession {
  7. protected static Boolean manualDirtyTrackingSupportEnabled = false;
  8. public static void setManualDirtyTrackingSupportEnabled(Boolean enabled) {
  9. manualDirtyTrackingSupportEnabled = enabled;
  10. }
  11. protected static String manualDirtyTrackingAttributeKey = "__changed__";
  12. public static void setManualDirtyTrackingAttributeKey(String key) {
  13. manualDirtyTrackingAttributeKey = key;
  14. }
  15. protected HashMap<String, Object> changedAttributes;
  16. protected Boolean dirty;
  17. public RedisSession(Manager manager) {
  18. super(manager);
  19. resetDirtyTracking();
  20. }
  21. public Boolean isDirty() {
  22. return dirty || !changedAttributes.isEmpty();
  23. }
  24. public HashMap<String, Object> getChangedAttributes() {
  25. return changedAttributes;
  26. }
  27. public void resetDirtyTracking() {
  28. changedAttributes = new HashMap<String, Object>();
  29. dirty = false;
  30. }
  31. @Override
  32. public void setAttribute(String key, Object value) {
  33. if (manualDirtyTrackingSupportEnabled && manualDirtyTrackingAttributeKey.equals(key)) {
  34. dirty = true;
  35. return;
  36. }
  37. Object oldValue = getAttribute(key);
  38. if ( value == null && oldValue != null
  39. || oldValue == null && value != null
  40. || !value.getClass().isInstance(oldValue)
  41. || !value.equals(oldValue) ) {
  42. changedAttributes.put(key, value);
  43. }
  44. super.setAttribute(key, value);
  45. }
  46. @Override
  47. public void removeAttribute(String name) {
  48. dirty = true;
  49. super.removeAttribute(name);
  50. }
  51. @Override
  52. public void setId(String id) {
  53. this.id = id;
  54. }
  55. @Override
  56. public void setPrincipal(Principal principal) {
  57. dirty = true;
  58. super.setPrincipal(principal);
  59. }
  60. }

3、session处理器实现

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

  1. package cn.eguid.redisSessionManager;
  2. import org.apache.catalina.Session;
  3. import org.apache.catalina.connector.Request;
  4. import org.apache.catalina.connector.Response;
  5. import org.apache.catalina.valves.ValveBase;
  6. import javax.servlet.ServletException;
  7. import java.io.IOException;
  8. public class RedisSessionHandlerValve extends ValveBase {
  9. // redis-session-manager管理器操作
  10. private RedisSessionManager manager;
  11. // 通过tomcat的context.xml可以注入该实例
  12. public void setRedisSessionManager(RedisSessionManager manager) {
  13. this.manager = manager;
  14. }
  15. // 产生一个请求后
  16. @Override
  17. public void invoke(Request request, Response response) throws IOException, ServletException {
  18. try {
  19. getNext().invoke(request, response);
  20. } finally {
  21. System.out.println("请求完毕后,redis-session-manager正在获取当前产生的session");
  22. Session session = request.getSessionInternal(false);
  23. storeOrRemoveSession(session);
  24. System.out.println("redis-session-manager操作结束,正在清理内存中的session!");
  25. // 删除内存中的session
  26. manager.afterRequest();
  27. }
  28. }
  29. private void storeOrRemoveSession(Session session) {
  30. try {
  31. if (session!=null && session.isValid() && session.getSession() != null) {
  32. manager.save(session);
  33. } else {
  34. manager.remove(session);
  35. }
  36. } catch (Exception e) {
  37. System.err.println("提示一下:session操作失败");
  38. }
  39. }
  40. }

二、如何配置该项目到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. tomcat集群和负载均衡的实现(session同步)

      (一)环境说明 (1)服务器有4台,一台安装apache,三台安装tomcat (2)apache2.0.55.tomcat5.5.15.jk2.0.4.jdk1.5.6或jdk1.4.2 (3) ...

  2. 【原创】Tomcat集群环境下对session进行外部缓存的方法(2)

    Session对象的持久化比较麻烦,虽然有序列化,但是并不确定Session对象中保存的其他信息是否可以序列化,这可能是网上很多解决方案摒弃此种做法的原因,网上的很多做法都是将Session中的att ...

  3. 19.Tomcat集群架构

    1.Nginx+Tomcat集群架构介绍 2.Nginx+Tomcat集群架构实战 [root@lb01 conf.d]# cat proxy_zrlog.cheng.com.conf upstrea ...

  4. Nginx+Tomcat集群部署

    为了获取更好的性能,我们常常需要将tomcat进行集群部署.下文通过nginx转发实现tomcat集群,并通过nginx-upstream-jvm-route插件保证session的粘滞. 应用场景环 ...

  5. tomcat集群与负载均衡

    参考文章http://kalogen.iteye.com/blog/784908,加上了自己调试过程中遇到的问题. 注1:实现此集群的方法参考了网上的很多文章,但由于很多文章都表明是原创的,故无法知道 ...

  6. nginx反向代理和tomcat集群(适用于ubutnu16.04及其centos7)

    下面示例,本人亲测有效 为什么要反向代理和集群? 因为并发问题,很多请求如果全部分发给一个tomcat,一个tomcat优化最好的话,据说可达到800负载,但是面对成千上万的请求,单单一个tomcat ...

  7. linux搭建tomcat集群+nginx

    安装JDK 一.官方下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下 ...

  8. Tomcat集群搭建超详细(apache+mod_jk+tomcat)

    TOMCAT集群 目录 TOMCAT集群 1 1 集群 1 1.1 什么是集群 1 1.2 集群的特性 1 1.3 集群的分类 1 1.4 TOMCAT集群配置的优缺点 2 1.5 APACHE+TO ...

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

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

  10. 基于Redis的CAS服务端集群

    为了保证生产环境CAS(Central Authentication Service)认证服务的高可用,防止出现单点故障,我们需要对CAS Server进行集群部署. CAS的Ticket默认是以Ma ...

随机推荐

  1. Angular 路由⑦要素

    cnzt       http://www.cnblogs.com/zt-blog/p/7919185.html http://www.cnblogs.com/zt-blog/p/7919185.ht ...

  2. jQuery学习总结(一)——jQuery基础与学习资源

    前一段时间录了一套关于jQuery的视频分享给大家,可以在下载区下载到,本来想配合文字一起的,后面发现视频+帮助文档也是非常好的学习方法. 一.jQuery简介与第一个jQuery程序 1.1.jQu ...

  3. 使用git 高效多人合作

    复习一下... 附加学习链接: http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/) ...

  4. Golang Global Variable access

    golang 中全局变量的问题. ------------------------------------------------------------------ 17down votefavor ...

  5. python各种类型转换

    python各种类型转换 学习了:https://blog.csdn.net/shanliangliuxing/article/details/7920400 https://blog.csdn.ne ...

  6. [NPM] Set default values for package.json using npm set

    Npm by default uses global values when initializing a new package.json file. Learn how to set your o ...

  7. BZOJ 2809 APIO 2012 dispatching 平衡树启示式合并

    题目大意:给出一棵树,每个节点有两个值,各自是这个忍者的薪水和忍者的领导力.客户的惬意程度是这个点的领导力乘可以取得人数.前提是取的人的薪水总和不超过总的钱数. 思路:仅仅能在子树中操作.贪心的想,我 ...

  8. time machine不备份指定文件夹

    osx中常常会使用timemachine来备份一些文件,timemachine能够使某个文件夹恢复到之前某个时刻的状态,很的方便.但是备份须要空间,特别是有些我们并不想备份一些无关紧要的文件,比方电影 ...

  9. swift,demo,ios8

    swift交流群:342581988,欢迎增加. 刚刚写的小 demo.搞得还是不是太好.请大家拍砖! 能够直接复制执行 import UIKit class ViewController: UIVi ...

  10. SpringBoot学习之启动报错【This application has no explicit mapping for /error.....】

    今天做SpringBoot小例子,在请求controller层的时候出现如下问题. Whitelabel Error Page This application has no explicit map ...