在web应用中,我们经常会用session来保存已登录用户的相关信息,在单机应用中,由于所有的用户都访问同一个应用,而session都保存在此单机应用中所以并无不妥。但是随着用户并发量的上升,分布式系统势在必行,这就导致一个用户的访问请求可能会分发到不同的集群部署应用上处理,此时在某个应用上创建session存储信息可能换一个应用就找不到了。
那么如何解决这种问题呢?有三种思路:
1、当创建一份session时,给集群内所有应用都复制一份,很显然这种方法是很占用网络带宽和内存的;
2、利用负载均衡策略中的一致性hash,将同一个客户的请求都分发到同一个应用中,因此应用总能找到此次用户连接对应的session,例如nginx就可以配置ip_hash实现此功能
3、基于分布式全局缓存的全局session,例如我们可以利用redis、memcached全局缓存来存储session信息,以sessionId为key,当应用需要用到session数据时统一从全局缓存中获取
其实我们可以把session看做一个map,里边存储一些键值对数据信息。因此redis可以用hash来存储session信息,当然我们也可以利用String数据类型来存储,这时需要先把session信息转换成json串格式,然后再以key-value形式存储。现在我们就以String存储为例说说如何实现全局session:
此处配置了jedisPool来管理redis连接,防止并发太高导致资源过度占用
package cache;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisPoolManager {
private static int jedis_port = 6379;
private static String jedis_host = "127.0.0.1";
private static JedisPool jedisPool = null;
static{
JedisPoolConfig config = new JedisPoolConfig();
//配置最大jedis实例数
config.setMaxTotal(1000);
//配置资源池最大闲置数
config.setMaxIdle(200);
//等待可用连接的最大时间10s
config.setMaxWaitMillis(10*1000);
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的
config.setTestOnBorrow(true);
jedisPool = new JedisPool(jedis_host, jedis_port);
}
protected synchronized static Jedis getJedis(){
return jedisPool.getResource();
}
}
package cache;
import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
/**
* 利用redis缓存全局session,session转化为json串存入缓存,并设置超时时间
* @author smart
*
*/
public class RedisCacheUtil extends RedisPoolManager{
/**
* 根据key获取session中的值
* @param sessionId
* @param key
* @return
*/
public static Object getSessionValue(String sessionId,String key){
Jedis jedis = getJedis();
if(jedis.exists(sessionId)){
String sessionStr = jedis.get(sessionId);
JSONObject session = JSONObject.parseObject(sessionStr);
return session.get(key);
}
return null;
}
/**
* 往session中放入entry
* @param sessionId
* @param key
* @param value
* @param expireTime 超时时间
*/
public static void setSessionValue(String sessionId,String key,String value,int expireTime){
Jedis jedis = getJedis();
try{
if(jedis.exists(sessionId)){
String sessionStr = jedis.get(sessionId);
JSONObject session = JSONObject.parseObject(sessionStr);
session.put(key, value);
jedis.set(sessionId, session.toString());
jedis.expire(sessionId, expireTime);
}else{
JSONObject session = new JSONObject();
session.put(key, value);
jedis.set(sessionId, session.toString());
jedis.expire(sessionId, expireTime);
}
}finally{
jedis.close();
}
}
/**
* 根据sessionid查询全局session是否存在
* @param sessionId
* @return
*/
public boolean isExistSession(String sessionId){
Jedis jedis = getJedis();
try{
return jedis.exists(sessionId);
}finally{
jedis.close();
}
}
/**
* 根据sessionId删除session
* @param sessionId
*/
public void removeSession(String sessionId){
Jedis jedis = getJedis();
try{
jedis.del(sessionId);
}finally{
jedis.close();
}
}
public void createSession(){
}
/**
* 获取JsessionId
* @param request
* @return
*/
public static String getJsessionId(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
// 从Cookie数据中遍历查找 并取CSESSIONID
if (null != cookies && cookies.length > 0) {
for (Cookie cookie : cookies) {
if ("JSESSIONID".equals(cookie.getName())) {
// 有 直接返回
return cookie.getValue().toString();
}
}
}
return null;
}
public static String addCookie(HttpServletRequest request, HttpServletResponse response, Integer KEY_EXPIRE_TIME) {
String jsessionId = getJsessionId(request);
if (jsessionId == null || "".equals(jsessionId)) {
jsessionId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
}
Cookie cookie = new Cookie("CSESSIONID", jsessionId);
cookie.setPath("/");
cookie.setMaxAge(KEY_EXPIRE_TIME);
response.addCookie(cookie);
return jsessionId;
}
}