redis中分布式session不一致性怎么办

分布式session不一致性怎么办?下面本篇文章给大家介绍一下redis中分布式session不一致性的解决方案,希望对大家有所帮助!

redis中分布式session不一致性怎么办

分布式session不一致性解决方案

一、Session有什么作用?

Session 是客户端与服务器通讯会话跟踪技术,服务器与客户端保持整个通讯的会话基本信息。【相关推荐:Redis视频教程】

客户端在第一次访问服务端的时候,服务端会响应一个sessionId并且将它存入到本地cookie中,在之后的访问会将cookie中的sessionId放入到请求头中去访问服务器,

如果通过这个sessionId没有找到对应的数据,那么服务器会创建一个新的sessionId并且响应给客户端。

二、分布式Session有什么问题?

单服务器web应用中,session信息只需存在该服务器中,这是我们前几年最常接触的方式

但是近几年随着分布式系统的流行,单系统已经不能满足日益增长的百万级用户的需求,集群方式部署服务器已在很多公司运用起来

当高并发量的请求到达服务端的时候通过负载均衡的方式分发到集群中的某个服务器,这样就有可能导致同一个用户的多次请求被分发到集群的不同服务器上,就会出现取不到session数据的情况,于是session的共享就成了一个问题。

1.png

三、服务做集群一般是怎么样做的?

SpringBoot项目,那么只要改下端口号启动几个,然后用nginx统一做反向代理。SpringCloud微服务项目,那么这个时候,可以使用ribbon本地负载均衡。

四、nginx负载均衡和ribbon负载均衡的区别

nginx做负载均衡是服务器端的负载均衡,统一访问一个地址,根据负载均衡算法访问决定访问那一个服务器。ribbon负载均衡,这是本地负载均衡(客户端负载均衡),把提供服务的客户端地址都缓存记录下来,根据本地的算法实现负载均衡。

五、Session一致性解决方案

1. session复制(同步)

2.png

思路:多个服务端之间相互同步session,这样每个服务端之间都包含全部的session

优点:服务端支持的功能,应用程序不需要修改代码

缺点:

session的同步需要数据传输,占内网带宽,有时延所有服务端都包含所有session数据,数据量受内存限制,无法水平扩展

2. 客户端存储法 

3.png

思路:服务端存储所有用户的session,内存占用较大,可以将session存储到浏览器cookie中,每个端只要存储一个用户的数据了

优点:服务端不需要存储

缺点:

每次http请求都携带session,占外网带宽数据存储在端上,并在网络传输,存在泄漏、篡改、窃取等安全隐患session存储的数据大小和域名cookie个数都受限制的

注:该方案虽然不常用,但确实是一种思路。

3. 反向代理hash一致性

 思路:服务端为了保证高可用,有多台冗余,反向代理层能不能做一些事情,让同一个用户的请求保证落在一台服务端上呢?

方案一:四层代理hash

4.png

反向代理层使用用户的ip来做hash,以保证同一个ip的请求落在同一个服务端上

方案二:七层代理hash

5.png

反向代理使用http协议中的某些业务属性来做hash,例如sid,city_id,user_id等,能够更加灵活的实施hash策略,以保证同一个浏览器用户的请求落在同一个服务器上

优点:

只需要改nginx配置,不需要修改应用代码负载均衡,只要hash属性是均匀的,多台服务端的负载是均衡的可以支持服务端水平扩展(session同步法是不行的,受内存限制)

缺点:

如果服务端重启,一部分session会丢失,产生业务影响,例如部分用户重新登录如果服务端水平扩展,rehash后session重新分布,也会有一部分用户路由不到正确的session

session一般是有有效期的,所有不足中的两点,可以认为等同于部分session失效,一般问题不大。

对于四层hash还是七层hash,个人推荐前者:让专业的软件做专业的事情,反向代理就负责转发,尽量不要引入应用层业务属性,除非不得不这么做(例如,有时候多机房多活需要按照业务属性路由到不同机房的服务器)。

四层、七层负载均衡的区别

4. 后端统一集中存储

6.png

优点:

没有安全隐患可以水平扩展,数据库/缓存水平切分即可服务端重启或者扩容都不会有session丢失

不足:增加了一次网络调用,并且需要修改应用代码

对于db存储还是cache,个人推荐后者:session读取的频率会很高,数据库压力会比较大。如果有session高可用需求,cache可以做高可用,但大部分情况下session可以丢失,一般也不需要考虑高可用。

总结

保证session一致性的架构设计常见方法:

session同步法:多台服务端相互同步数据客户端存储法 一个用户只存储自己的数据反向代理hash一致性 四层hash和七层hash都可以做,保证一个用户的请求落在一台服务端上后端统一存储 服务端重启和扩容,session也不会丢失(推荐后端cache统一存储)

六、案例实战:SpringSession+redis解决分布式session不一致性问题

步骤1:加入SpringSession、redis的依赖包

    org.springframework.boot    spring-boot-starter-redis    1.4.7.RELEASE    org.springframework.session    spring-session-data-redis

登录后复制

步骤2:配置文件

# 为某个包目录下 设置日志logging.level.com.ljw=debug# 设置session的存储方式,采用redis存储spring.session.store-type=redis# session有效时长为10分钟server.servlet.session.timeout=PT10M## Redis 配置## Redis数据库索引(默认为0)spring.redis.database=0## Redis服务器地址spring.redis.host=127.0.0.1## Redis服务器连接端口spring.redis.port=6379## Redis服务器连接密码(默认为空)spring.redis.password=

登录后复制

步骤3: 配置拦截器

@Configurationpublic class SessionConfig implements WebMvcConfigurer {    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(new SecurityInterceptor())                //排除拦截的2个路径                .excludePathPatterns("/user/login")                .excludePathPatterns("/user/logout")                //拦截所有URL路径                .addPathPatterns("/**");    }}

登录后复制

@Configurationpublic class SecurityInterceptor implements HandlerInterceptor {    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {        HttpSession session = request.getSession();        //验证当前session是否存在,存在返回true true代表能正常处理业务逻辑        if (session.getAttribute(session.getId()) != null){            log.info("session拦截器,session={},验证通过",session.getId());            return true;        }        //session不存在,返回false,并提示请重新登录。        response.setCharacterEncoding("UTF-8");        response.setContentType("application/json; charset=utf-8");        response.getWriter().write("请登录!!!!!");        log.info("session拦截器,session={},验证失败",session.getId());        return false;    }}

登录后复制HandlerInterceptorpreHandle:在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理;postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndViewafterCompletion:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面)

步骤4: 控制器

@RestController@RequestMapping(value = "/user")public class UserController {    Map userMap = new HashMap();    public UserController() {        //初始化2个用户,用于模拟登录        User u1=new User(1,"user1","user1");        userMap.put("user1",u1);        User u2=new User(2,"user2","user2");        userMap.put("user2",u2);    }    @GetMapping(value = "/login")    public String login(String username, String password, HttpSession session) {        //模拟数据库的查找        User user = this.userMap.get(username);        if (user != null) {            if (!password.equals(user.getPassword())) {                return "用户名或密码错误!!!";            } else {                session.setAttribute(session.getId(), user);                log.info("登录成功{}",user);            }        } else {            return "用户名或密码错误!!!";        }        return "登录成功!!!";    }    /**     * 通过用户名查找用户     */    @GetMapping(value = "/find/{username}")    public User find(@PathVariable String username) {        User user=this.userMap.get(username);        log.info("通过用户名={},查找出用户{}",username,user);        return user;    }    /**     *拿当前用户的session     */    @GetMapping(value = "/session")    public String session(HttpSession session) {        log.info("当前用户的session={}",session.getId());        return session.getId();    }    /**     * 退出登录     */    @GetMapping(value = "/logout")    public String logout(HttpSession session) {        log.info("退出登录session={}",session.getId());        session.removeAttribute(session.getId());        return "成功退出!!";    }}

登录后复制

步骤5: 实体类

@Datapublic class User implements  Serializable{    private int id;    private String username;    private String password;    public User(int id, String username, String password) {        this.id = id;        this.username = username;        this.password = password;    }}

登录后复制

步骤6:访问测试

先登录:http://www.php.cn/course/list/54.html

再查询http://www.php.cn/course/list/54.html

七、剖析SpringSession的redis原理

步骤1:分析SpringSession的redis数据结构

127.0.0.1:6379> keys *1) "spring:session:sessions:9889ccfd-f4c9-41e5-b9ab-a77649a7bb6a"2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b"3) "spring:session:expirations:1635413520000"4) "spring:session:sessions:expires:9889ccfd-f4c9-41e5-b9ab-a77649a7bb6a"5) "spring:session:expirations:1635412980000"6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

登录后复制

共同点:3个key都是以spring:session:开头的,代表了SpringSession的redis数据。

查询类型

127.0.0.1:6379> type spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3bhash

登录后复制

127.0.0.1:6379> hgetall spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b// session的创建时间1) "creationTime"2) "��srjava.lang.Long;��̏#�Jvaluexrjava.lang.Number�����xp|���u"// sesson的属性,存储了user对象3) "sessionAttr:d3434f61-4d0a-4687-9070-610bd7790f3b"4) "��srcom.ljw.redis.controller.User"_m�WIidLpasswordtLjava/lang/String;Lusernameq~xptuser1q~"//最后的访问时间5) "lastAccessedTime"6) "��srjava.lang.Long;��̏#�Jvaluexrjava.lang.Number�����xp|����"//失效时间 100分钟7) "maxInactiveInterval"8) "��srjava.lang.Integer⠤���8Ivaluexrjava.lang.Number�����xpp"

登录后复制

步骤2:分析SpringSession的redis过期策略

对于过期数据,一般有三种删除策略:

定时删除,即在设置键的过期时间的同时,创建一个定时器, 当键的过期时间到来时,立即删除。

惰性删除,即在访问键的时候,判断键是否过期,过期则删除,否则返回该键值。

定期删除,即每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

redis删除过期数据采用的是懒性删除+定期删除组合策略,也就是数据过期了并不会及时被删除。

但由于redis是单线程,并且redis对删除过期的key优先级很低;如果有大量的过期key,就会出现key已经过期但是未删除。

为了实现 session 过期的及时性,spring session 采用了定时删除+惰性删除的策略。

定时删除

127.0.0.1:6379> type spring:session:expirations:1635413520000set127.0.0.1:6379> smembers  spring:session:expirations:16354135200001) "��t,expires:d3434f61-4d0a-4687-9070-610bd7790f3b"

登录后复制

7.png

2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 3) "spring:session:expirations:1635413520000" 6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

登录后复制登录后复制1635412980000 是时间戳,等于 2021-10-28 17:23:00,即是该可以在这个时刻过期springsession 定时(1分钟)轮询,删除spring:session:expirations:[?] 的过期成员元素,例如:spring:session:expirations:1635413520000springsesion 定时检测超时的key的值,根据值删除seesion,例如key:spring:session:expirations:1635413520000,值为(sessionId):d3434f61-4d0a-4687-9070-610bd7790f3b的seesion

惰性删除

127.0.0.1:6379> type spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3bstring127.0.0.1:6379> get spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b""127.0.0.1:6379> ttl spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b(integer) 3143127.0.0.1:6379>

登录后复制访问 spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b的时候,判断key是否过期,过期则删除,否则返回改进的值。例如 访问spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b的时候,判断 ttl 是否过期,过期就直接删除

2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 3) "spring:session:expirations:1635413520000" 6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

登录后复制登录后复制

更多编程相关知识,请访问:编程视频!!

以上就是redis中分布式session不一致性怎么办的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2039805.html

(0)
上一篇 2025年2月24日 00:05:31
下一篇 2025年2月24日 00:05:52

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 浅析Redis中AOF的原理和缺点

    本篇文章带大家了解一下redis持久化中的aof,介绍一下aof原理、aof缺点,希望对大家有所帮助! AOF 上文我们提到了rRedis中的一种持久化方式就是RDB 此文我们来讲另一种实现方式那就是AOF AOF是通过只记录Redis写入…

    2025年2月24日
    000
  • Redis中什么是慢查询、订阅模式

    本篇文章给大家介绍一下redis中的慢查询和订阅模式,希望对大家有所帮助! 慢查询 慢查询日志就是系统在命令执行时每条命令的执行时间,当超过阀值,就将这条命令记录下来。【相关推荐:Redis视频教程】 Redis命令执行流程 发送命令 命令…

    2025年2月24日 数据库
    200
  • 深入了解Redis中的主从同步机制

    本篇文章带大家了解一下redis中的主从同步,介绍一下redis主从的两种结构模型、主从关系的建立、主从复制策略等,希望对大家有所帮助! 之前的文章中详细分析了redis的特性和核心原理,从本篇开始将对redis的部署结构和运行模式进行分析…

    2025年2月24日 数据库
    200
  • 浅析Python中怎么用Redis

    python中怎么用redis?下面本篇文章给大家介绍一下python使用redis的方法,希望对大家有所帮助! 前面我们都是使用 Redis 客户端对 Redis 进行使用的,但是实际工作中,我们大多数情况下都是通过代码来使用 Redis…

    2025年2月24日 数据库
    200
  • 深入聊聊Redis中的5种基本数据类型

    本篇文章带大家详细了解一下redis中的5种基本数据类型(string字符串、list列表、set集合、hash散列、zset有序集合),希望对大家有所帮助! Redis数据结构简介 对redis来说,所有的key(键)都是字符串。我们在谈…

    2025年2月24日
    200
  • 聊聊Redis中的缓存穿透、缓存雪崩、缓存击穿和缓存一致性

    本篇文章带大家简单了解一下redis中的缓存穿透、缓存雪崩、缓存击穿和缓存一致性,介绍一下缓存穿透和缓存雪崩的解决方案,希望对大家有所帮助! 缓存雪崩 缓存同一时间大面积失效,后面的请求都会落到数据库上,造成数据库短时间内无法承受大量请求而…

    2025年2月24日
    200
  • 聊聊Redis的持久化机制,到底采用RDB还是AOF呢?

    本篇文章带大家了解一下redis的持久化机制(rdb和aof),聊聊到底采用rdb还是aof呢?希望对大家有所帮助! RDB 1. 什么是RDB RDB:每隔一段时间,把内存中的数据写入磁盘的临时文件,作为快照,恢复的时候把快照文件读进内存…

    2025年2月24日
    200
  • 浅谈Redis中缓存过期、内存被缓存占用要怎么处理?

    浅谈redis中缓存过期、内存被缓存占用要怎么处理?下面本篇文章带大家了解一下redis中的缓存过期处理策略和内存淘汰机制,希望对大家有所帮助! 已过期的key如何处理? 设置了expire的key缓存过期了,但是服务器的内存还是会被占用,…

    2025年2月24日
    200
  • 聊聊Redis中的哨兵模式(Sentine)

    本篇文章带大家了解一下redis中的哨兵模式(sentine),介绍一下sentinel工作机制、sentinel模式怎么搭建,希望对大家有所帮助! Redis Sentinel哨兵模式 是一个分布式系统, 你可以在一个架构中运行多个 Se…

    2025年2月24日
    200
  • Redis如何实现分布式锁?聊聊实现方法

    如何使用 redis 实现分布式锁?下面本篇文章给大家介绍一下基于 redis 实现分布式锁的方法,希望对大家有所帮助! 在一个分布式系统中,会遇到一些需要对多个节点共享的资源加锁的情况,这个时候需要用到分布式锁。分布式锁通常保存在一个共享…

    2025年2月24日
    200

发表回复

登录后才能评论