一、Cookie机制和Session机制回顾
1)定义:Session成为“会话”,具体是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间。Session实际上是一个特定的时间概念。
 
2)HTTP协议与状态保持:HTTP 协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。
 
cookie的作用就是为了解决HTTP协议无状态的缺陷。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。
 
3)Cookie和Session机制的区别和联系(几个有趣的例子):
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。
 
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。(Cookie原理)
 
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。(Session原理)
 
由于HTTP协议是无状态的,cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。
 
二、集群下实现Session共享的几种方案
 
1.请求精确定位:基于IP地址的Hash策略,将同一用户的请求都集中在一台服务器上,这台服务器上保存了该用户的Session信息。缺点:单点部署发生宕机时,Session丢失。
 
2.Session复制共享:比如可以用Tomcat自带的插件进行Session同步,使得多台应用服务器之间自动同步Session,保持一致。如果一台发生故障,负载均衡会遍历寻找可用节点,Session也不会丢失。缺点:必须是Tomcat和Tomcat之间,Session的复制也会消耗系统 的性能,使得同步给成员时容易造成内网流量瓶颈。
 
3.基于cache DB缓存的Session共享(推荐,Spring-Session也是同样的原理,同自定义的JRedis一起配置可以实现目的):使用Redis存取Session信息,应用服务器发生故障时,当Session不在内存中时就会去CacheDB中查找(要求Redis支持持久化),找到则复制到本机,实现Session共享和高可用。
 
分布式Session配置原理图
 

 
其他方式:利用公共的NFS服务器做共享服务器、完全利用Cookie(将Session数据全放在Cookie中)等。
 
三、基于Redis的Session共享实现(核心代码)
 
1)原理:写一个Session过滤器拦截每一次请求,在这里检查由Cookie生成的SessionID,进行创建或获取。核心是实现使用装饰类,实现Session在Redis中的存取操作。
2)此处存取方式为 sessionID+sessionKey作为Redis的key  ==== sessionValue作为Redis的value,这样保存了每次存取都从Redis中操作,效率更高。
3)注意:序列化方式推荐使用Apache下Commons组件——SerializationUtils 或 org.springframework.util.SerializationUtils

 1 //定义请求经过的Session过滤器
public class SessionFilter extends OncePerRequestFilter implements Filter {
@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 = UUID.randomUUID().toString();
CookieUtil.setCookie(request, response, GlobalConstant.JSESSIONID, sid, 60 * 60);
}
// 交给自定义的HttpServletRequestWrapper处理
filterChain.doFilter(new HttpServletRequestWrapper(sid, request, response), response);
}
}
     //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);
} 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 "";
}
     //SessionService实现   sidKey == sessionID+SessionKey
public Object getSession(String sidKey) {
Object realValue = null;
try {
String key = “SESSION_DISTRIBUTED_SESSIONID” + sidKey;
realValue = SerializeUtil.unserialize(RedisUtils.getInstance().get(key.getBytes()));
} catch (Exception e) {
LOG.error("Redis获取session异常" + e.getMessage(), e.getCause());
}
return realValue;
} public void saveSession(String sidKey, Object value) {
try {
String key = “SESSION_DISTRIBUTED_SESSIONID” + sidKey;
boolean isSetSuccess = RedisUtils.getInstance().set(key.getBytes(), SerializeUtil.serialize(value));
if (!isSetSuccess) {
LOG.error("Redis保存session异常");
}
} catch (Exception e) {
LOG.error("Redis保存session异常" + e.getMessage(), e.getCause());
}
} public void removeSession(String sidKey) {
try {
String key =“SESSION_DISTRIBUTED_SESSIONID”+ sidKey;
RedisUtils.getInstance().del(key.getBytes());
} catch (Exception e) {
LOG.error("Redis删除session的attribute异常" + e.getMessage(), e.getCause());
}
} public void removeAllSession(String sid) {
try {
String keyPattern =“SESSION_DISTRIBUTED_SESSIONID” + sid + "*";
Set<byte[]> keys = RedisUtils.getInstance().keys(keyPattern.getBytes());
for (byte[] key : keys) {
RedisUtils.getInstance().del(key);
}
} catch (Exception e) {
LOG.error("Redis删除session异常" + e.getMessage(), e.getCause());
}
} public Set<String> getAllKeys(String sid) {
try {
Set<String> keysResult = new HashSet<String>();
String keyPattern =“SESSION_DISTRIBUTED_SESSIONID” + sid + "*";
Set<byte[]> keys = RedisUtils.getInstance().keys(keyPattern.getBytes()); for (byte[] key : keys) {
keysResult.add(new String(key));
}
return keysResult;
} catch (Exception e) {
LOG.error("Redis删除session异常" + e.getMessage(), e.getCause());
return null;
}
}
 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;
}
     HttpSessionWrapper implements HttpSession{

     private String sid = "";

     private HttpSession session;

     private HttpServletRequest request;

     private HttpServletResponse response;

     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;
} @Override
public Object getAttribute(String name) {
return sessionService.getSession(this.sid+"#"+name);
} @Override
public void setAttribute(String name, Object value) {
sessionService.saveSession(this.sid+"#"+name, value);
} @Override
public void invalidate() {
sessionService.removeAllSession(this.sid);
CookieUtil.removeCookieValue(this.request,this.response, GlobalConstant.JSESSIONID);
} @Override
public void removeAttribute(String name) {
sessionService.removeSession(this.sid+"#"+name);
} @Override
public Object getValue(String name) {
return this.session.getValue(name);
} @SuppressWarnings("unchecked")
@Override
public Enumeration getAttributeNames() {
return (new Enumerator(sessionService.getAllKeys(this.sid), true));
} @Override
public String getId() {
return this.sid;
}
69 }

集群环境下的Session共享的更多相关文章

  1. 集群环境下,Session管理的几种手段

    集群环境下,Session管理的几种手段 1.Session复制 缺点:集群服务器间需要大量的通信进行Session复制,占用服务器和网络的大量资源. 由于所有用户的Session信息在每台服务器上都 ...

  2. weblogic 12C集群环境下的session复制

    做过weblogic集群环境的人应该都清楚,要想实现session同步,必须满足两个条件:第一,在weblogic.xml里面增加session同步相关的代码:第二,所有放入session的类都要序列 ...

  3. 集群环境下的Session管理

    1. 集群环境下的管理HTTPSSession所遇到的问题 一台服务器对应这个一个session对象,无法在另外一个服务器互通 解决方法: 1. Session 的 Replication(复制)将当 ...

  4. nginx+php负载均衡集群环境中的session共享方案梳理

    在网站使用nginx+php做负载均衡情况下,同一个IP访问同一个页面会被分配到不同的服务器上,如果session不同步的话,就会出现很多问题,比如说最常见的登录状态. 下面罗列几种nginx负载均衡 ...

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

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

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

    BJJC网改版, 计划将应用部署在tomcat集群上,集群的部署方案为Apache+Tomcat6,连接件为mod_jk,其中开启了session复制和粘性session.计划节点数为3个. 到这,或 ...

  7. 集群环境下Shiro Session的管理

    问题引入 紧接上篇连接 在多台tomcat集群中,shiro管理的session需要放在Redis中,我们只需要增加redisSessionDAO的配置就行 <!-- 定义会话管理器的操作 表示 ...

  8. Shiro权限管理框架(二):Shiro结合Redis实现分布式环境下的Session共享

    首发地址:https://www.guitu18.com/post/2019/07/28/44.html 本篇是Shiro系列第二篇,使用Shiro基于Redis实现分布式环境下的Session共享. ...

  9. redis 与java的连接 和集群环境下Session管理

    redis 的安装与设置开机自启(https://www.cnblogs.com/zhulina-917/p/11746993.html)  第一步: a) 搭建环境 引入 jedis jar包 co ...

随机推荐

  1. mybatis操作mysql的奇淫技巧总结(代码库)

    1.添加公共sql代码段 使用<sql> 和 <include> 标签 <sql id="userSubassemblyRecordParam"> ...

  2. Python中的string和bytes的转换

    总的来说,bytes和string的关系是: \(bytes\xrightarrow{decode}string\) \(bytes\xleftarrow{encode}string\) 常见的几种编 ...

  3. Java【第四篇】基本语法之--循环

    循环语句功能 在循环条件满足的情况下,反复执行特定代码 循环语句的四个组成部分 初始化部分(init_statement)循环条件部分(test_exp) 循环体部分(body_statement) ...

  4. Java【第十篇】集合

    Java 集合概述 Java 集合就像一种容器,可以把多个对象的引用放入容器中.Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组Java 集合可分为 Set.List ...

  5. [TJOI2015]概率论

    [TJOI2015]概率论 史上最短黑题 看起来一脸懵逼,没有取模,1e-9 根据期望定义,发现 分母是一个卡特兰数,,,,不能直接算 所以考虑怎么消掉一些东西 gn表示n个点的叶子个数和,fn表示n ...

  6. Web of science数据下载以数据处理

    目标网站分析 我们要获取的就是这几个数值 程序实现 # -*- coding: utf-8 -*- """ @Datetime: 2019/2/28 @Author: Z ...

  7. 第四节:Task的启动的四种方式以及Task、TaskFactory的线程等待和线程延续的解决方案

    一. 背景 揭秘: 在前面的章节介绍过,Task出现之前,微软的多线程处理方式有:Thread→ThreadPool→委托的异步调用,虽然也可以基本业务需要的多线程场景,但它们在多个线程的等待处理方面 ...

  8. WEB内容换行

    word-wrap:break-word 单词间换行 word-break:break-all 单词内也可以换行 white-space属性指定元素内的空白怎样处理 normal 默认.空白会被浏览器 ...

  9. Mathematica 求出解后代入变量

    Solve[2 x - 3 == 0, x] x = x //. %[[1]]

  10. Hibernate 4.3.11 下问题的解决

    2017.01.09 问题:hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hib ...