最近项目设计集群,实现了一下session的共享功能,其原理是将session保存到分布式缓存数据库中如:redis, memcache等,然后多个服务器tomcat
每次请求都通过NoSql数据库查询,如果存在,则获取值;反之存放值。
我是通过redis来实现session的共享,其主要有一下两种方法:
1、通过tomcat服务器的拓展功能实现
这种方式比较简单,主要是通过继承session的ManagerBase类,实现重写session相关的方法,这种比较简单,
参考源码链接(http://download.csdn.net/detail/fengshizty/9417242)。
2、通过filter拦截request请求实现
下面主要介绍这样实现方式:
(1)写HttpSessionWrapper实现HttpSession接口,实现里面session相关的方法。
(2)写HttpServletRequestWrapper继承javax.servlet.http.HttpServletRequestWrapper类,重写对于session 相关的方法。
(3)写SessionFilter拦截配置的请求url,过去cookie中
的sessionId,如果为空,对此次请求重写生成一个新的sessionId,在sessionId构造新的HttpServletRequestWrapper对象。
(4)写SessionService实现session到redis的保存和过去,其key为sessionId,value为session对于的Map。
3、代码实现
(1)HttpSessionWrapper
/**
* 创建时间:2016年1月21日 下午7:55:41
*
* @author andy
* @version 2.2
*/
public class HttpSessionWrapper implements HttpSession {
private String sid = "";
private HttpSession session;
private HttpServletRequest request;
private HttpServletResponse response;
private Map<String, Object> map = null;
private SessionService sessionService = (SessionService) SpringContextHolder.getBean("sessionService");
public HttpSessionWrapper() {
}
public HttpSessionWrapper(HttpSession session) {
this.session = session;
}
public HttpSessionWrapper(String sid, HttpSession session) {
this(session);
this.sid = sid;
}
public HttpSessionWrapper(String sid, HttpSession session,
HttpServletRequest request, HttpServletResponse response) {
this(sid, session);
this.request = request;
this.response = response;
}
private Map<String, Object> getSessionMap() {
if (this.map == null) {
this.map = sessionService.getSession(this.sid);
}
return this.map;
}
@Override
public Object getAttribute(String name) {
if (this.getSessionMap() != null) {
Object value = this.getSessionMap().get(name);
return value;
}
return null;
}
@Override
public void setAttribute(String name, Object value) {
this.getSessionMap().put(name, value);
sessionService.saveSession(this.sid, this.getSessionMap());
}
@Override
public void invalidate() {
this.getSessionMap().clear();
sessionService.removeSession(this.sid);
CookieUtil.removeCookieValue(this.request,this.response, GlobalConstant.JSESSIONID);
}
@Override
public void removeAttribute(String name) {
this.getSessionMap().remove(name);
sessionService.saveSession(this.sid, this.getSessionMap());
}
@Override
public Object getValue(String name) {
return this.session.getValue(name);
}
@SuppressWarnings("unchecked")
@Override
public Enumeration getAttributeNames() {
return (new Enumerator(this.getSessionMap().keySet(), true));
}
@Override
public String[] getValueNames() {
return this.session.getValueNames();
}
@Override
public void putValue(String name, Object value) {
this.session.putValue(name, value);
}
@Override
public void removeValue(String name) {
this.session.removeValue(name);
}
@Override
public long getCreationTime() {
return this.session.getCreationTime();
}
@Override
public String getId() {
return this.sid;
}
@Override
public long getLastAccessedTime() {
return this.session.getLastAccessedTime();
}
@Override
public ServletContext getServletContext() {
return this.session.getServletContext();
}
@Override
public void setMaxInactiveInterval(int interval) {
this.session.setMaxInactiveInterval(interval);
}
@Override
public int getMaxInactiveInterval() {
return this.session.getMaxInactiveInterval();
}
@Override
public HttpSessionContext getSessionContext() {
return this.session.getSessionContext();
}
@Override
public boolean isNew() {
return this.session.isNew();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
(2)HttpServletRequestWrapper实现
/**
* 创建时间:2016年1月22日 下午7:52:29
*
* @author andy
* @version 2.2
*/
public class HttpServletRequestWrapper extends
javax.servlet.http.HttpServletRequestWrapper {
private HttpSession session;
private HttpServletRequest request;
private HttpServletResponse response;
private String sid = "";
public HttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
public HttpServletRequestWrapper(String sid, HttpServletRequest request) {
super(request);
this.sid = sid;
}
public HttpServletRequestWrapper(String sid, HttpServletRequest request,
HttpServletResponse response) {
super(request);
this.request = request;
this.response = response;
this.sid = sid;
if (this.session == null) {
this.session = new HttpSessionWrapper(sid, super.getSession(false),
request, response);
}
}
@Override
public HttpSession getSession(boolean create) {
if (this.session == null) {
if (create) {
this.session = new HttpSessionWrapper(this.sid,
super.getSession(create), this.request, this.response);
return this.session;
} else {
return null;
}
}
return this.session;
}
@Override
public HttpSession getSession() {
if (this.session == null) {
this.session = new HttpSessionWrapper(this.sid, super.getSession(),
this.request, this.response);
}
return this.session;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
(3)SessionFilter拦截器的实现
public class SessionFilter extends OncePerRequestFilter implements Filter {
private static final Logger LOG = Logger.getLogger(SessionFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//从cookie中获取sessionId,如果此次请求没有sessionId,重写为这次请求设置一个sessionId
String sid = CookieUtil.getCookieValue(request, GlobalConstant.JSESSIONID);
if(StringUtils.isEmpty(sid) || sid.length() != 36){
sid = StringUtil.getUuid();
CookieUtil.setCookie(request, response, GlobalConstant.JSESSIONID, sid, 60 * 60);
}
//交给自定义的HttpServletRequestWrapper处理
filterChain.doFilter(new HttpServletRequestWrapper(sid, request, response), response);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
(4)SessionService实现session从redis的读写存储
public class SessionService {
private final static Logger LOG = Logger.getLogger(SessionService.class);
private JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();
@Autowired
private RedisTemplate<Serializable, Serializable> redisTemplate;
@SuppressWarnings("unchecked")
public Map<String, Object> getSession(String sid) {
Map<String, Object> session = new HashMap<String, Object>();
try {
Object obj = redisTemplate.opsForValue()
.get(RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid);
if(obj != null){
obj = jdkSerializer.deserialize((byte[])obj);
session = (Map<String, Object>) obj;
}
} catch (Exception e) {
LOG.error("Redis获取session异常" + e.getMessage(), e.getCause());
}
return session;
}
public void saveSession(String sid, Map<String, Object> session) {
try {
redisTemplates.opsForValue()
.set(RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid,
jdkSerializer.serialize(session), RedisKeyUtil.SESSION_TIMEOUT,
TimeUnit.MINUTES);
} catch (Exception e) {
LOG.error("Redis保存session异常" + e.getMessage(), e.getCause());
}
}
public void removeSession(String sid) {
try {
redisTemplates.delete(
RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid);
} catch (Exception e) {
LOG.error("Redis删除session异常" + e.getMessage(), e.getCause());
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
(5)Session的拦截配置,一般的我们只需要拦截我们定义的拦截请求拦截,而不需要所有的都需要拦截。在web.xml中配置SessionFilter。
<filter>
<filter-name>sessionFilter</filter-name>
<filter-class>org.andy.shop.session.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sessionFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
附:设计到的工具类
1、StringUtil工具类
/**
* String工具类
*
* @author andy
* @date 2015-5-16 下午4:04:22
*
*/
public class StringUtil {
private StringUtil() {
super();
}
/**
* 出去null和""
* @param src
* @return
*/
public static String formatNull(String src) {
return (src == null || "null".equals(src)) ? "" : src;
}
/**
* 判断字符串是否为空的正则表达式,空白字符对应的unicode编码
*/
private static final String EMPTY_REGEX = "[\\s\\u00a0\\u2007\\u202f\\u0009-\\u000d\\u001c-\\u001f]+";
/**
* 验证字符串是否为空
*
* @param input
* @return
*/
public static boolean isEmpty(String input) {
return input == null || input.equals("") || input.matches(EMPTY_REGEX);
}
public static boolean isNotEmpty(String input){
return !isEmpty(input);
}
public static String getUuid() {
return UUID.randomUUID().toString();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
2、Cookie管理CookieUtil工具类
/**
* 创建时间:2016年1月22日 下午8:33:56
*
* @author andy
* @version 2.2
*/
public class CookieUtil {
private static final String KEY = "jkdflsffff()kldkjapfdY=::$B+DUOWAN";
private HttpServletRequest request;
private HttpServletResponse response;
private static String domain = "andy.com";
public CookieUtil(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
/**
* 保存cookie
* @param request
* @param response
* @param name cookie名称
* @param value cookie值
* @param seconds 过期时间(单位秒) -1代表关闭浏览器时cookie即过期
*/
public static void setCookie(HttpServletRequest request,
HttpServletResponse response, String name, String value, int seconds) {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value))
return;
Cookie cookie = new Cookie(name, value);
//cookie.setDomain(domain);
cookie.setMaxAge(seconds);
cookie.setPath("/");
response.setHeader("P3P",
"CP='IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT'");
response.addCookie(cookie);
}
/**
* 过去cookie中保存的值
* @param name
* @return
* @throws UnsupportedEncodingException
*/
public String getCookieValue(String name)
throws UnsupportedEncodingException {
Cookie cookies[] = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (name.equalsIgnoreCase(cookies[i].getName())) {
return cookies[i].getValue();
}
}
}
return "";
}
/**
* 设置加密的cookie
* @param name cookie名称
* @param value cookie值
* @param seconds 过期时间 -1代表关闭浏览器时cookie即过期
*/
public void setCheckCodeCookie(String name, String value, int seconds) {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) {
return;
}
String md5Value = MD5Utils.getMD5(KEY + value);
Cookie cookie = new Cookie(name, md5Value);
//cookie.setDomain(domain);
cookie.setMaxAge(seconds);
cookie.setPath("/");
response.addCookie(cookie);
}
/**
* 校验加密的cookie
* @param name
* @param value
* @return
*/
public boolean checkCodeCookie(String name, String value) {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) {
return false;
}
boolean result = false;
String cookieValue = getCookieValue(request, name);
if (MD5Utils.getMD5(KEY + value).equalsIgnoreCase(
cookieValue)) {
result = true;
}
return result;
}
/**
* 获取cookie值
* @param request
* @param name
* @return
*/
public static String getCookieValue(HttpServletRequest request, String name) {
try {
Cookie cookies[] = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (name.equalsIgnoreCase(cookies[i].getName())) {
return cookies[i].getValue();
}
}
}
} catch (Exception e) {
}
return "";
}
/**
* 移除客户端的cookie
* @param request
* @param response
* @param name
*/
public static void removeCookieValue(HttpServletRequest request,
HttpServletResponse response, String name) {
try {
Cookie cookies[] = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (name.equalsIgnoreCase(cookie.getName())) {
cookie = new Cookie(name, null);
cookie.setMaxAge(0);
cookie.setPath("/");
//cookie.setDomain(domain);
response.addCookie(cookie);
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
3、redis键key设置类RedisKeyUtil
/**
* 创建时间:2015年9月22日 下午4:51:11
*
* @author andy
* @version 2.2
*/
public class RedisKeyUtil {
public static final String SESSION_DISTRIBUTED_SESSIONID = "session:distributed:"; //分布式session sessionid -- sessionvalue
public static final Integer SESSION_TIMEOUT = 2; //session 失效时间2小时
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
4、分布式session常量设置类GlobalConstant
/**
* 创建时间:2016年1月23日 上午11:16:56
*
* 分布式session常量
*
* @author andy
* @version 2.2
*/
public class GlobalConstant {
public static final String USER_SESSION_KEY = "user_session_key";//用户session信息
public static final String JSESSIONID = "YHMJSESSIONID"; //jsessionid
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
分布式session在redis执行结果: