文档章节

利用Redis共享Tomcat中的Session

囚徒困境
 囚徒困境
发布于 2016/10/18 22:26
字数 782
阅读 493
收藏 5

想要做Tomcat集群,其中需要解决的一个问题就是多个Tomcat中session的共享。共享的方法有很多种,比如使用Tomcat自带的session复制,使用数据库等。这里一些介绍我使用过的方法。

1.替换Tomcat的sessionManager

这种方法实现起来比较容易,但是需要改动每个Tomcat服务器的配置。对于Tomcat6和7,可以使用tomcat-redis-session-manager库来实现,对于Tomcat8以及8以上的版本,可以搜索对应的库。

tomcat-redis-session-manager(Github):https://github.com/jcoleman/tomcat-redis-session-manager

库的介绍里有详细的使用方法,这里再简单列一下:

  1. 修改  TOMCAT_BASE/conf  目录下的  context.xml  文件,示例配置如下:
    <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标签之前

  2. 将以下依赖包拷贝到  TOMCAT_BASE/lib  目录
  • tomcat-redis-session-manager-VERSION.jar
  • jedis-2.5.2.jar
  • commons-pool2-2.2.jar

重启Tomcat后生效。

 

如果项目中使用了Spring和Apache Shiro,可以使用下面的方法。

2.继承Shiro的AbstractSessionDAO

序列化工具示例:

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示例:

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>

 

© 著作权归作者所有

囚徒困境
粉丝 15
博文 25
码字总数 11216
作品 0
深圳
程序员
私信 提问
nginx 解决session一致性

session 粘滞性 每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 但是有缺点,这存在单点风险,倘若我已经在192.168.0.14:88端口登录后,过段...

zhu_kai1
2018/11/21
845
0
nginx负载均衡实现tomcat集群方案简要小结

重点两部分:一、负载均衡二、tomcat集群 所谓tomcat集群,就是可以向外提供并行服务的多台机器,任何一台服务器宕机,其它服务器可以替代它向外提供服务,而不影响用户访问。 nginx是一个常...

天下杰论
2015/03/11
1K
1
nginx-tomcat负载均衡redis-session共享,静态资源分离

nginx-tomcat负载均衡redis-session共享,静态资源分离 基本环境: redis-2.8 Apache-tomcat-6.0.41 nginx1.6.2 1,redis配置 1,配置redis访问密码 到redis的目录下找的redis.conf,解开req...

steven
2016/07/29
51
0
Springboot和Spring Session实现session共享

HttpSession是通过Servlet容器创建和管理的,像Tomcat/Jetty都是保存在内存中的。而如果我们把web服务器搭建成分布式的集群,然后利用LVS或Nginx做负载均衡,那么来自同一用户的Http请求将有...

ben4
2017/10/19
0
0
tomcat8 nginx负载均衡 + 动静资源分离, 利用redis 共享 session

1、基本环境: tomcat-8.0 2台 (端口 8180, 8280) redis-2.8 (port:6379 ) nginx-1.8 2、添加tomcat和redis做session共享的jar包 (在tomcat自己的lib下添加session共享所需的jar包,需要...

steven
2016/07/29
117
0

没有更多内容

加载失败,请刷新页面

加载更多

排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
今天
4
0
mysql概览

学习知识,首先要有一个总体的认识。以下为mysql概览 1-架构图 2-Detail csdn |简书 | 头条 | SegmentFault 思否 | 掘金 | 开源中国 |

程序员深夜写bug
今天
9
0
golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web

micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go-micro环境, golang微服务框架...

非正式解决方案
今天
6
0
前端——使用base64编码在页面嵌入图片

因为页面中插入一个图片都要写明图片的路径——相对路径或者绝对路径。而除了具体的网站图片的图片地址,如果是在自己电脑文件夹里的图片,当我们的HTML文件在别人电脑上打开的时候图片则由于...

被毒打的程序猿
今天
8
0
Flutter 系列之Dart语言概述

Dart语言与其他语言究竟有什么不同呢?在已有的编程语言经验的基础上,我们该如何快速上手呢?本篇文章从编程语言中最重要的组成部分,也就是基础语法与类型变量出发,一起来学习Dart吧 一、...

過愙
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多