前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springboot之session集成redis

springboot之session集成redis

作者头像
叔牙
发布2020-11-19 17:43:47
9420
发布2020-11-19 17:43:47
举报
文章被收录于专栏:一个执拗的后端搬砖工

springboot之session集成redis

背景介绍

我们正常境况下写的应用部署到服务器中,session会话是默认存储到服务器内存中,内存的优点是读写比较快,正如jvm内存是java世界中最快的内存一样,但是任何事物都有其两面性,内存session的缺点和优点一样明显,这里我们简要分析一下内存session的优缺点:

  • 优点 读取快 不存在一致性问题(单点)
  • 缺点 当会话量足够大时,会打爆应用内存 无法做集群

由于传统的单体架构已经远远支撑不了当前互联网井喷式发展所带来的巨大流量,所以目前新的应用架构一定会考虑分布式和集群,那么会面临很多之前单体架构不存在的问题和挑战。

比较明显的就是:

  • session共享
  • 请求路由
  • 数据路由

我们用一张图来描述传统的单体架构简单粗暴的换成集群部署锁面临的问题:

  1. 用户请求过来后如何确定路由到哪台服务器上?
  2. 用户请求落到多台服务器上是不是要每台服务器都存储一份会话?
  3. 用户读写操作如何路由到指定的DB?

对于上述三个问题,本篇不会做全部解答,会主要针对问题2做详细分析和介绍。

问题抛出

针对上边描述的问题2,细化衍生成两个问题:

  1. session分离存储问题
  2. session共享问题

接下来我们将通过代码和实际案例来解答上述问题,并实现使用redis存储会话和解决会话共享问题。

会话分离存储

传统的单体架构,在会话量暴增的时候,可能会导致应用内存爆掉,导致jvm内存溢出,那么单纯解决这个问题相对比较简单,我们可以把session从应用内存中抽出来单独存储。

首先先看一下应用内存session存储模式:

并发量大的时候,红色区域占用存储空间会暴增,从而打爆应用内存,导致应用崩溃。

再看一下会话分离存储模式:

session会话会单独存储到外部存储器中,这样会话量暴涨的时候,只会占用redis存储空间,不会对应用造成影响。

实现

1.新建spingboot应用&添加依赖

<!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- session-redis --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>

启动类:

@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }

2.新建application.properties文件

#redis spring.redis.database=0 spring.redis.host=host spring.redis.port=port spring.redis.password=password # session spring.session.store-type=redis

3.编写session配置

@Configuration public class SessionConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SecurityInterceptor()) //排除拦截 .excludePathPatterns("/user/login") .excludePathPatterns("/user/logout") //拦截路径 .addPathPatterns("/**"); } @Configuration public class SecurityInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { HttpSession session = request.getSession(); if (session.getAttribute(session.getId()) != null){ return true; } response.getWriter().write(JSON.toJSONString("please login first")); return false; } } }

4.编写业务代码&测试

@RestController @RequestMapping("/user") public class IndexController { @GetMapping("/login") public String login(String account, String password,HttpSession session) { if("typhoon".equalsIgnoreCase(account) && "123".equalsIgnoreCase(password)) { session.setAttribute(session.getId(),User.builder() .account(account) .password(password) .build()); return "success"; } return "failed"; } @GetMapping(value = "/logout") public String logout(HttpSession session){ session.removeAttribute(session.getId()); return "user logout success"; } @GetMapping(value = "/{account}") public User getUser(@PathVariable("account") String account) { return User.builder() .account(account) .build(); } }

请求之前redis中key列表:

发送登录请求:

请求之后redis中key列表:

这样我们就基于springboot和redis实现了session会话分离存储。

session会话共享

session会话分离存储解决了并发场景的应用内存问题,但是在应用集群部署情况下存在session会话共享问题,基于上一节的实现,如果应用集群部署会存在同一用户请求路由到不同服务器产生多份session问题,造成存储空间浪费:

如果集群服务器共享session会话,那么将会节省很多存储空间,并且省去用户路由到新的服务器上的session重新生成带来的登录失效问题:

实现

  1. 在上一节SessionConfig代码中增加@EnableRedisHttpSession注解:

@Configuration @EnableRedisHttpSession public class SessionConfig implements WebMvcConfigurer { // }

2.编写业务代码

在controller中增加以下代码:

@RequestMapping(value = "/first", method = RequestMethod.GET) public Map<String, Object> firstResp (HttpServletRequest request){ Map<String, Object> map = new HashMap<>(); request.getSession().setAttribute("request Url", request.getRequestURL()); map.put("request Url", request.getRequestURL()); return map; } @RequestMapping(value = "/sessions", method = RequestMethod.GET) public Object sessions (HttpServletRequest request){ Map<String, Object> map = new HashMap<>(); map.put("sessionId", request.getSession().getId()); map.put("message", request.getSession().getAttribute("map")); return map; }

3.打包并启动服务

打包并分别使用两个端口启动服务

4.测试验证

启动之后先通过8080端口访问服务,返回地址:

{"request Url":"http://localhost:8080/user/first"}

接着,我们访问8080端口的sessions,返回:

{"sessionId":"efcc85c0-9ad2-49a6-a38f-9004403776b5","message":"http://localhost:8080/user/first"}

最后,再访问9090端口的sessions,返回:

{"sessionId":"efcc85c0-9ad2-49a6-a38f-9004403776b5","message":"http://localhost:8080/user/first"}

由此可见,8080与9090两个服务器返回结果一样,实现了session的共享,也就是说通过此种方式可以实现集群情况下session共享。

总结

本篇我们介绍了传统session存储方式存在的弊端,以及通过实际案例实现了session分离存储和解决了集群环境下session共享问题,从宏观层面上也分析了从传统单体架构到目前分布式集群架构所面临和需要解决的问题。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PersistentCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档