背景:对于秒杀服务,实际的应用可能部署在不止一个服务器上,而是分布式的多台服务器,这时候要是用户登录是在第一个服务器,但是操作的时候第二个请求在第二个服务器,就会丢失用户的Session信息,最少你的重新登录。
一。分布式Session的几种实现方式
1.基于数据库的Session共享
2.基于NFS共享文件系统3.基于memcached 的session,如何保证 memcached 本身的高可用性?
4. 基于resin/tomcat web容器本身的session复制机制
5. 基于TT/Redis 或 jbosscache 进行 session 共享。
6. 基于cookie 进行session共享
链接:转载自:http://blog.csdn.net/u014352080/article/details/51764311 分布式Session的几种实现方式
本文的方式:利用一台缓存服务器集中管理session。
缓存集中式管理
简介:将Session存入分布式缓存集群中的某台机器上,当用户访问不同节点时先从缓存中拿Session信息
使用场景:集群中机器数多、网络环境复杂
优点:可靠性好
缺点:实现复杂、稳定性依赖于缓存的稳定性、Session信息放入缓存时要有合理的策略写入
相对还有其他几种: 各有优缺点
Session Replication 方式管理 (即session复制)
简介:将一台机器上的Session数据广播复制到集群中其余机器上
使用场景:机器较少,网络流量较小
优点:实现简单、配置较少、当网络中有机器Down掉时不影响用户访问
缺点:广播式复制到其余机器有一定廷时,带来一定网络开销
Session Sticky 方式管理
简介:即粘性Session、当用户访问集群中某台机器后,强制指定后续所有请求均落到此机器上
使用场景:机器数适中、对稳定性要求不是非常苛刻
优点:实现简单、配置方便、没有额外网络开销
缺点:网络中有机器Down掉时、用户Session会丢失、容易造成单点故障
一、Session Replication 方式管理 (即session复制)
简介:将一台机器上的Session数据广播复制到集群中其余机器上
使用场景:机器较少,网络流量较小
优点:实现简单、配置较少、当网络中有机器Down掉时不影响用户访问
缺点:广播式复制到其余机器有一定廷时,带来一定网络开销
二、Session Sticky 方式管理
简介:即粘性Session、当用户访问集群中某台机器后,强制指定后续所有请求均落到此机器上
使用场景:机器数适中、对稳定性要求不是非常苛刻
优点:实现简单、配置方便、没有额外网络开销
缺点:网络中有机器Down掉时、用户Session会丢失、容易造成单点故障
本文实现:
/*到达这里说明登陆成功 * 需要保存 相关 session信息写入 cookie 用于登陆*/ /*封装一个addCookie 方法 方便 重用*/ String token = UUIDUtill.uuid(); addCookie(miaoshaUser,token,response);//登陆成功 写入token ,同时写入 缓存和 cookie 之中 return true; }
本项目的做法:
在登录完成的最后一步,需要带着Session信息。 1.利用uuid生成秘钥(sessionId) 2.将user信息,对象。同时写入cookie cookie作为response返回给客户端, 另外 将sessionId +前缀 一起作为Key,存入Redis 缓存中。当访问其他页面的时候,就可以从cookie中获取 token,再访问redis 拿到用户信息来判断登录情况了。
(后续这个还有可以优化----解析参数,在获取token后在WebConfig中直接加入这个session的方法来拿到user)
private void addCookie(MiaoshaUser miaoshaUser,String token,HttpServletResponse response){ // String token = UUIDUtill.uuid(); 当我们延长有效期的时候 没有必要每次都生成新的token 直接延用 老的就可以了 /*将用户sessionId和用户信息 以键值形式 保存到 redis 中*/ redisService.set(MiaoshaUserKey.token,token,miaoshaUser); /*cookie 键值对都是 String类型*/ Cookie cookie = new Cookie(COOKIE_TOKEN_NAME,token); /*设置cookie的过期时间*/ cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds()); /*cookie 保存路径*/ cookie.setPath("/"); /*需要一个 response 对象 将cookie 返回给 客户端*/ response.addCookie(cookie); } }
Session 有效期处理,将cookie的有效期,设置在缓存的有效期。
2个关键的地方:1.再次访问,延长session有效期,即我们取到user对象之后,先不返回,再利用token 添加到缓存中,就可以实现每次访问页面都会延长时间。
public MiaoshaUser getByToken(HttpServletResponse response,String token) { if (StringUtils.isEmpty(token)){ return null; } /*根据token 和前缀 去redis 中取出 对应的 用户信息值*/ MiaoshaUser user = redisService.get(MiaoshaUserKey.token,token,MiaoshaUser.class); /*延长有效期*/ if (user!=null){ addCookie(user,token,response); } return user; }
优化:
虽然已经实现分布式session,但是每次都要访问页面都要通过cookie中拿到token,然后token 再去redis中取出user信息,来判断登录情况。很麻烦。
我们知道SpringMVC中的controller 方法中可以有很多参数,有些参数不需要传值,就可以直接获取(解析到),这里就会用到addArguementResolver() 和 HandlerMethodArguementResolver类。
HandlerMethodArgumentResolver
/* 通过 重写 springmvc 配置 方法 达到 无需传递参数的效果 自动获取 user 从session 中*/ @Configuration public class WebConfig extends WebMvcConfigurerAdapter{ @Autowired private UserArguementResolver userArguementResolver; @Autowired AccessInterceptor accessInterceptor; /*解析参数*/ @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(userArguementResolver); }
/*要将 MiaoshaUser 类 参数对象 注册 进来*/ @Service public class UserArguementResolver implements HandlerMethodArgumentResolver { @Autowired private MiaoshaUserService miaoshaUserService; /* 判断是否是MiaoshaUser 类 */ @Override public boolean supportsParameter(MethodParameter methodParameter) { Class<?> clazz = methodParameter.getParameterType();/*获取参数类型*/ return clazz == MiaoshaUser.class; /* 若是 MiaoshaUser 类 才进行下一步*/ } /*具体操作 获取到user 对象的参数 值 */ @Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { return UserContext.getUser();
一、Session Replication 方式管理 (即session复制)
简介:将一台机器上的Session数据广播复制到集群中其余机器上
使用场景:机器较少,网络流量较小
优点:实现简单、配置较少、当网络中有机器Down掉时不影响用户访问
缺点:广播式复制到其余机器有一定廷时,带来一定网络开销
二、Session Sticky 方式管理
简介:即粘性Session、当用户访问集群中某台机器后,强制指定后续所有请求均落到此机器上
使用场景:机器数适中、对稳定性要求不是非常苛刻
优点:实现简单、配置方便、没有额外网络开销
缺点:网络中有机器Down掉时、用户Session会丢失、容易造成单点故障
做法:重写上述2个方法:
就是遍历Controller 参数列表,如果有User这个参数,就解析他,然后根据request cookie 拿到我们cookie 在从cookie
取缓存,并赋值给这个参数。
/*第一步 : 取出request ,response */ HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class); HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class); /*第二步 : 取出token */ String paramToken = request.getParameter(MiaoshaUserService.COOKIE_TOKEN_NAME); String cookieToken = getCookieValue(request,MiaoshaUserService.COOKIE_TOKEN_NAME); if (StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {/*如果 cookie 中都没有值 返回 null 此时返回的 值 是给 MiaoshaUser 对象的 就是解析的参数值*/ return null; } /*有限从paramToken 中取出 cookie值 若没有从 cookieToken 中取*/ String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken; return miaoshaUserService.getByToken(response,token);/*拿到 user 对象*/ // } // private String getCookieValue(HttpServletRequest request, String cookieTokenName) { /*在 请求中 遍历所有的cookie 从中取到 我们需要的那一个cookie 就可以的*/ Cookie[] cookies = request.getCookies(); /*请求中没有cookies 的时候返回null ?? 没有cookie ? 没有登录吗?*/ if (cookies == null || cookies.length ==0) { return null; } for (Cookie cookie: cookies) { if (cookie.getName().equals(cookieTokenName)) return cookie.getValue(); } return null; }