目录
分布式环境下session会存在的问题?
图解:
文字解:
出现这个问题的原因从根本上来说是因为Http协议是⽆状态的协议。客户端和服务端在某次会话中产⽣的数据不会被保留下来,所以第⼆次请求服务端⽆法认识到你曾经来过, Http为什么要设计为⽆状态协议?早期都是静态⻚⾯⽆所谓有⽆状态,后来有动态的内容更丰富,就需要有状态,出现了两种⽤于保持Http状态的技术,那就是Cookie和Session。⽽出现上述不停让登录的问题,第一访问到tomcat1,登录成功sesssion中保持登录信息,
例如刷新页面nginx轮询到tomcat2,然而tomcat2并没有tomcat1的session,所以重定向到登录页面重新登录。
分布式Session⼀致性的⽅案
一.Nginx的 IP_Hash 策略(可以使⽤)
同⼀个客户端IP的请求都会被路由到同⼀个⽬标服务器,也叫做会话粘滞。
优点:
配置简单,不⼊侵应⽤,不需要额外修改代码。
缺点:
1.服务器重启Session丢失
2.存在单点负载⾼的⻛险
3.单点故障问题
二.Session复制(不推荐)
多个tomcat之间通过修改配置⽂件,达到Session之间的复制
优点:
1.不⼊侵应⽤
2.便于服务器⽔平扩展
3.能适应各种负载均衡策略
4.服务器重启或者宕机不会造成Session丢失
缺点:
1.性能低
2.内存消耗
3.不能存储太多数据,否则数据越多越影响性能
4.延迟性
三.Session共享,Session集中存储(推荐)
图解:
优点:
1.能适应各种负载均衡策略
2.服务器重启或者宕机不会造成Session丢失
3.扩展能⼒强
4.适合⼤集群数量使⽤
缺点:
1.对应⽤有⼊侵,引⼊了和Redis的交互代码
Spring Session使得基于Redis的Session共享应⽤起来⾮常之简单
使用步骤:
1.导入maven坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> |
2.配置redis(以自己的ip为主)
spring.redis.database=0 spring.redis.host=127.0.0.1 spring.redis.port=6379 |
3.在SpringBoot启动类型添加注解@EnableRedisHttpSession
package com.yang; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.session.data.redis.config.ConfigureRedisAction; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; @SpringBootApplication @EnableCaching @EnableRedisHttpSession public class LoginApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(LoginApplication.class); } public static void main(String[] args) { SpringApplication.run(LoginApplication.class, args); } @Bean public static ConfigureRedisAction configureRedisAction() { return ConfigureRedisAction.NO_OP; } } |
4.使用案例
/** * 请求权限验证 */ public class RequestInterceptor implements HandlerInterceptor { /** * 之前执行(进入Handler处理之前) * 可以进行权限验证 * @param request * @param response * @param handler * @return true放行,false中止程序 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); System.out.println("==========>>>>sessionId:" + session.getId()); System.out.println("======SESSION::::" + session); Enumeration<String> attrs = session.getAttributeNames(); // 遍历attrs中的 while(attrs.hasMoreElements()){ // 获取session键值 String name = attrs.nextElement().toString(); // 根据键值取session中的值 Object vakue = session.getAttribute(name); // 打印结果 System.out.println("------" + name + ":" + vakue +"--------\n"); } System.out.println("===============>>>>>>当前uri:" + request.getRequestURI()); Object username = session.getAttribute("username"); System.out.println("=================>>>>>username:" + username); if(username == null) { // 没有登录,重定向到登录页 System.out.println("未登录,请登录"); response.sendRedirect(request.getContextPath() + "/login/toLogin"); return false; }else{ System.out.println("已登录,放行请求"); // 已登录,放行 return true; } } /** * 之中执行(Handler处理完毕但尚未跳转页面) * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /** * 之后执行(Handler处理完毕而且已经跳转页面) * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } } |