在分布式系统中.前提是分布式或者集群环境,常常需要多个系统中,保持Session . nginx可以配置IP的hash,实现每次都访问同一台应用容器,从而不需要共享Session ,也有tomcat的复制. 虽然tomcat等容器可以实现Session 的复制,但是在tomcat的数量过多时,复制的延迟和性能的损失 将因为tomcat的数量直线上升. 这里可以用redis简单的将Session 集中管理.线上环境将redis高可用即可.
Java对Redis的操作可以用 Jedis.spring-data-redis封装Jedis,更加易用 本文采用 spring data redis作为Redis的连接工具. 采用Sping管理. Maven pom.xml引入
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
</dependency>
spring-redis.xml 配置 Redis的连接池和连接
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.pool.maxIdle}" />
<property name="maxTotal" value="${redis.pool.maxTotal}" />
<property name="timeBetweenEvictionRunsMillis" value="${redis.pool.timeBetweenEvictionRunsMillis}" />
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
</bean>
<bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"
p:use-pool="true" p:pool-config-ref="poolConfig" />
<!-- <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
</bean> -->
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnFactory" />
redis.properties 连接的配置肯定是需要的
redis.host=x.x.x.x
redis.port=6379
redis.pass=
redis.maxWait=30000
redis.pool.maxTotal=1024
redis.pool.maxIdle=200
redis.pool.timeBetweenEvictionRunsMillis=30000
redis.pool.testOnBorrow=true
在需要使用的类中注入
@Resource
protected RedisTemplate<Serializable, Serializable> redisTemplate;
组装一下key,统一设置前缀,可以方便的管理key
private String getJointName(String sid) {
String key = RedisExpire.ONLINEUSER + ":" + sid;//":"为文件夹形式
return key;
}
准备常量
public class RedisExpire {
/**
* Session 超时时间为30分钟
*/
public final static Long ThirtyMinuteSecend= 30*60L;//秒
public final static String ONLINEUSER= "OnLineUser";
}
然后即可开始操作...如在登陆时写入redis并设置Session时长.
/**
* 添加在线用户
*
* @param sid 生成对用户的唯一id.即Session中的sessionid
* source 为来源为后续app预留
* @param user
* @return
* @throws Exception
*/
public boolean addOnLinuUser(final String sid, final onLineUserInfo user,
final String source) throws Exception {
if (user != null && sid.trim().length() > 0) {
final String key;
key = getJointName(sid);
Boolean falg = redisTemplate.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection)
throws DataAccessException {
/*这里是存入时,序列化操作,可选
* @SuppressWarnings("unchecked")
* RedisSerializer<onLineUserInfo> valueSerializer =
* (RedisSerializer<onLineUserInfo>) redisTemplate
* .getValueSerializer();
*/
connection.select(2);//切换redis的DB可以不需要,redis默认配置为0-15共16个库,可以通过这行代码实现切换
connection.setEx(key.getBytes(),
RedisExpire.ThirtyMinuteSecend,
stringToByte(JSONObject.toJSONString(user)));//序列化采用了fastjson
return true;
}
});
return falg;
}
return false;
}
同理删除和获取
/**
* 移除在线登陆用户
*
* @param sid
* source 为来源为后续app预留
* @return
* @throws Exception
*/
public boolean removeOnLinuUser(final String sid, final String source)
throws Exception {
if (sid != null) {
final String key;
key = getJointName(sid);
Boolean falg = redisTemplate.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection)
throws DataAccessException {
connection.select(2);
Long del = connection.del(key.getBytes());
if (del == 1) {
return true;
} else {
return false;
}
}
});
return falg;
}
return false;
}
/**
* 获取在线用户信息
*
* @param sid
* source 为来源为后续app预留
* @return
* @throws Exception
*/
public onLineUserInfo getOnLinuUser(String sid, final String source)
throws Exception {
final String key;
key = getJointName(sid);
onLineUserInfo userInfo = redisTemplate
.execute(new RedisCallback<onLineUserInfo>() {
public onLineUserInfo doInRedis(RedisConnection connection)
throws DataAccessException {
connection.select(2);
byte[] bs = connection.get(key.getBytes());
String byteToString = byteToString(bs);
onLineUserInfo userinfo = JSONObject.parseObject(
byteToString, onLineUserInfo.class);
/*if (userinfo != null) {
// 如果用户已登录,则增加在线时间
connection.expire(key.getBytes(),
RedisExpire.ThirtyMinuteSecend);
}*/
return userinfo;
}
});
return userInfo;
}
}
文中的 onLineUserInfo 即你需要放入Session的登陆对象,可以自行编辑
public class onLineUserInfo implements Serializable{
private String name;// 用户名字
..........
}
不管存入cookie还是重写url...系统请求是需要带上sid即可.
PS:有人问我为什么要用顶级的key,不用Hash,因为redis的key是有上限的.主要是只有顶级的key才能有过期时间一说.这里牵涉到Redis的一些内部特征,大家可以去查阅关于Redis的文章.