【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【19】认证服务03—分布式下Session共享问题

news2024/11/28 1:43:13

持续学习&持续更新中…

守破离


【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【19】分布式下Session共享问题

  • session原理
  • 分布式下session共享问题
  • Session共享问题解决—session复制
  • Session共享问题解决—客户端存储
  • Session共享问题解决—hash一致性
  • Session共享问题解决—统一存储
  • Session共享问题解决—不同服务,子域session共享
  • 手动设置Cookie,手动拿取Cookie
  • 整合SpringSession
  • SpringSession核心原理
  • 参考

session原理

在这里插入图片描述

问题:不能跨不同域名共享

在这里插入图片描述

分布式下session共享问题

在这里插入图片描述

在这里插入图片描述

Session共享问题解决—session复制

在这里插入图片描述

优点 :web-server(Tomcat)原生支持,只需要修改配置 文件

缺点 :

  • session同步需要数据传输,占用大量网络带宽,降低了服务器群的业务处理能力
  • 任意一台web-server保存的数据都是所有web- server的session总和,受到内存限制无法水平扩展更多的web-server
  • 大型分布式集群情况下,由于所有web-server都全量保存数据,所以此方案不可取。

Session共享问题解决—客户端存储

在这里插入图片描述

优点

  • 服务器不需存储session,用户保存自己的 session 信息到 cookie 中。节省服务端资源

缺点

  • 都是缺点,这只是一种思路。
  • 具体如下:
  • 每次http请求,携带用户在cookie中的完整信息, 浪费网络带宽
  • session数据放在cookie中,cookie有长度限制 4 K,不能保存大量信息
  • session数据放在cookie中,存在泄漏、篡改、 窃取等安全隐患
  • 这种方式不会使用。

Session共享问题解决—hash一致性

在这里插入图片描述

优点:

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

缺点:

  • session还是存在web-server中的,所以web-server重启可能导致部分session丢失,影响业务,如部分用户需要重新登录
  • 如果web-server水平扩展,rehash 后session 重新分布, 也会有一部分用户路由不到正确的session
  • 但是以上缺点问题也不是很大,因为session本来都是有有效期的。所以这两种反向代理的方式可以使用

Session共享问题解决—统一存储

在这里插入图片描述

优点:

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

不足:

  • 增加了一次网络调用,并且需要修改应用代码;如将所有的getSession方法替换为从Redis查数据的方式。
  • redis获取数据比内存慢很多
  • 上面缺点可以用SpringSession完美解决

Session共享问题解决—不同服务,子域session共享

jsessionid这个cookie默认是当前系统域名的。当我们分拆服务,不同域名部署的时候,我们可以使用如下解决方案;

放大Cookie作用域

在这里插入图片描述

手动设置Cookie,手动拿取Cookie

gulimall-auth:OAuth2Controller

    @GetMapping("/oauth2.0/weibo/success")
    public String weibo(@RequestParam("code") String code, HttpSession session,
                        HttpServletResponse httpServletResponse) throws Exception {
        Map<String, String> headers = new HashMap<>();
        Map<String, String> bodys = new HashMap<>();
        bodys.put("client_id", "3276999101");
        bodys.put("client_secret", "452bbefff4680ac8554b97799a8c12cb");
        bodys.put("grant_type", "authorization_code");
        bodys.put("redirect_uri", "http://auth.gulimall.com/oauth2.0/weibo/success");
        bodys.put("code", code);
        //1、根据code换取accessToken;
        HttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", headers, null, bodys);
        if (response.getStatusLine().getStatusCode() == 200) {
            //2、获取到了 socialUserAccessToken 进行处理
            String json = EntityUtils.toString(response.getEntity());
            SocialUserAccessToken socialUserAccessToken = JSON.parseObject(json, SocialUserAccessToken.class);
//            String uid = socialUserAccessToken.getUid();
            // 通过uid就知道当前是哪个社交用户
            //1)、当前用户如果是第一次进网站,进行自动注册(为当前社交用户生成一个会员信息账号,以后这个社交账号就对应指定的会员账号)
            R r = memberFeignService.socialLogin(socialUserAccessToken);
            if (r.getCode() == BizCodeEnume.SUCCESS.getCode()) {
                //登录或者注册这个社交用户
                //2)、登录成功就跳回首页

                /**
                 * 手动设置Cookie
                 */
                MemberRespVo loginUser = r.getData(new TypeReference<MemberRespVo>() {
                });
                stringRedisTemplate.opsForValue().set("loginUser", JSON.toJSONString(loginUser));
                Cookie cookie = new Cookie("GULIMALL", "loginUser");
                cookie.setDomain("gulimall.com");
                cookie.setMaxAge(24 * 60 * 60);
                cookie.setPath("/");
                httpServletResponse.addCookie(cookie);
                session.setAttribute("loginUser", loginUser);

                return "redirect:http://gulimall.com";
            }
        }
        return "redirect:http://auth.gulimall.com/login.html";
    }

gulimall-product:IndexController

    @GetMapping({"/", "/index.html"})
    public String indexPage(Model model, HttpServletRequest httpServletRequest, HttpSession session) {

        /**
         * 手动获取Cookie
         */
        Cookie[] cookies = httpServletRequest.getCookies();
        if (null != cookies && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equalsIgnoreCase("GULIMALL")) {
                    String loginUserKey = cookie.getValue();
                    String json = stringRedisTemplate.opsForValue().get(loginUserKey);
                    MemberRespVo loginUser = JSON.parseObject(json, new TypeReference<MemberRespVo>(){});
                    session.setAttribute("loginUser", loginUser);
                }
            }
        }

        List<CategoryEntity> categorys = categoryService.listLevel1Categorys();
        model.addAttribute("categorys", categorys);
        return "index";
    }

@Controller
public class LoginController {
    @GetMapping("/login.html")
    public String loginPage() {
        if(stringRedisTemplate.opsForValue().get("loginUser") != null) return "redirect:http://gulimall.com";
        return "login";
    }
}

整合SpringSession

<!-- 1 整合SpringSession完成session共享问题 -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
# 2 整合SpringSession
spring.session.store-type=redis
#server.servlet.session.timeout=60m
# 3 配置Redis的连接信息(之前配过)
#spring.redis.host=xxx
#spring.redis.port=xxx
#spring.redis.password=xxx
@EnableRedisHttpSession // 4 整合Redis作为session存储
// 5 使用SpringSession【跟以前使用session的写法一样】
//第一次使用session;命令浏览器保存卡号。JSESSIONID这个cookie;
//以后浏览器访问哪个网站就会带上这个网站的cookie;
//子域之间; gulimall.com  auth.gulimall.com  order.gulimall.com
//应该做到:发卡的时候(指定域名为父域名),那么,即使是子域系统发的卡,也能让父域直接使用。
// 1、默认发的令牌。session=xxxxxxx。作用域:当前域;(SpringSession默认没有解决子域session共享问题)
// 2、使用JSON的序列化方式来序列化对象数据到redis中
	R r = memberFeignService.socialLogin(socialUserAccessToken);
	if (r.getCode() == BizCodeEnume.SUCCESS.getCode()) {
	    //登录或者注册这个社交用户
	    //2)、登录成功就跳回首页
	
	MemberRespVo loginUser = r.getData(new TypeReference<MemberRespVo>() {
	});
	session.setAttribute("loginUser", loginUser);

//6 配置序列化 + Cookie domain
// 解决子域session共享问题
@Configuration
public class GulimallSessionConfig {

    @Bean
    public CookieSerializer cookieSerializer(){
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();

        cookieSerializer.setDomainName("gulimall.com");
        cookieSerializer.setCookieName("GULISESSION");
//        cookieSerializer.setCookieMaxAge(); // 默认是浏览器的session级别,关闭浏览器就失效

        return cookieSerializer;
    }

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }
    
}
<!-- 7 给其他服务也整合好SpringSession后,直接取session中的数据即可  -->
 <a  th:if="${session.loginUser!=null}">欢迎:[[${session.loginUser==null?'':session.loginUser.nickname}]]</a>
                    
//    登录页面
    @GetMapping("/login.html")
    public String loginPage(HttpSession session) {
//        if(stringRedisTemplate.opsForValue().get("loginUser") != null) return "redirect:http://gulimall.com";
        Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);
        if(attribute != null) return "redirect:http://gulimall.com";
        return "login";
    }

SpringSession核心原理

/**
 * SpringSession 核心原理 装饰者模式;
 * @EnableRedisHttpSession导入RedisHttpSessionConfiguration配置
 *      1、给容器中添加了一个组件
 *          SessionRepository = 》》》【RedisOperationsSessionRepository】==》redis操作session。session的增删改查封装类
 *      2、SessionRepositoryFilter == 》Filter: session'存储过滤器;每个请求过来都必须经过filter
 *          1、创建的时候,就自动从容器中获取到了SessionRepository;
 *          2、原始的request,response都被包装。SessionRepositoryRequestWrapper,SessionRepositoryResponseWrapper
 *          3、以后获取session。SessionRepositoryRequestWrapper.getSession();
 *          4、wrappedRequest.getSession();===> SessionRepository 中获取到的。
 *
 自动延期;用户只要没有关闭浏览器,SpringSession会自动续期,当然,用户关闭了浏览器,redis中的数据也是有过期时间的。
 */

在这里插入图片描述

参考

雷丰阳: Java项目《谷粒商城》Java架构师 | 微服务 | 大型电商项目.


本文完,感谢您的关注支持!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1897122.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】 解决 C++ 语言报错:Invalid Use of Incomplete Type

文章目录 引言 在 C 编程中&#xff0c;“Invalid Use of Incomplete Type” 是一种常见错误。此错误通常在程序试图使用未完全定义的类或结构时发生。这种错误不仅会导致编译失败&#xff0c;还可能导致程序行为不可预测。本文将详细探讨无效使用不完整类型的成因、检测方法及…

【Redis】真行,原来是这样啊! --Redis自动序列化和手动序列化的区别(存储结构、内存开销,实际写法)

对于Redis有两种序列化和反序列化的方式&#xff0c; 方式一&#xff1a; 一种是通过 注入RedisTemplate 对象&#xff0c;找个对象&#xff0c;通过配置类进行一定的配置&#xff0c;使得使用RedisTemplate 对象时&#xff0c;便会使用配置的那些键、值的序列化方式&#xff…

【人工智能】--强化学习(2.0)

个人主页&#xff1a;欢迎来到 Papicatch的博客 课设专栏 &#xff1a;学生成绩管理系统 专业知识专栏&#xff1a; 专业知识 文章目录 &#x1f349;强化学习与有监督学习的区别 &#x1f348;数据特点 &#x1f348;学习目标 &#x1f348;反馈机制 &#x1f348;策略…

数学建模MATLAB绘图大全

最近快要开始一年一度的数学建模竞赛啦&#xff0c;接下来争取每天更一篇数学建模算法&#xff01;&#xff08;当然这是理想状态下&#xff09;&#xff0c;今天就先更一些MATLAB常用的绘图吧&#xff0c;论文赏心悦目的关键就在于丰富多彩的图&#xff0c;好看的图一定会成为…

Linux 查看磁盘是不是 ssd 的方法

lsblk 命令检查 $ lsblk -d -o name,rota如果 ROTA 值为 1&#xff0c;则磁盘类型为 HDD&#xff0c;如果 ROTA 值为 0&#xff0c;则磁盘类型为 SSD。可以在上面的屏幕截图中看到 sda 的 ROTA 值是 1&#xff0c;表示它是 HDD。 2. 检查磁盘是否旋转 $ cat /sys/block/sda/q…

深入理解【 String类】

目录 1、String类的重要性 2、常用方法 2、1 字符串构造 2、2 String对象的比较 2、3 字符串查找 2、4字符转换 数值和字符串转换&#xff1a; 大小写转化&#xff1a; 字符串转数组&#xff1a; 格式转化&#xff1a; 2、5 字符串替换 2、6字符串拆分 2、7 字符串…

【pytorch12】什么是梯度

说明 导数偏微分梯度 梯度&#xff1a;是一个向量&#xff0c;向量的每一个轴是每一个方向上的偏微分 梯度是有方向也有大小&#xff0c;梯度的方向代表函数在当前点的一个增长的方向&#xff0c;然后这个向量的长度代表了这个点增长的速率 蓝色代表比较小的值&#xff0c;红色…

初阶数据结构二叉树练习系列(1)

这个系列的文章将带大家一起刷题&#xff0c;并且总结思路 温馨提示&#xff1a;本篇文章里的练习题仅适合刚学完二叉树的小白使用 相同的树 思路 情况分析&#xff1a;第一种情况&#xff1a;两棵树都为空 → 返回true 第二种情况&am…

Vue +Echarts 二维数组获取最大值和最小值以及索引

实现图例效果 获取最大值方法&#xff1a; maxSecond(array) {let max Number.MIN_SAFE_INTEGER;for (let i 0; i < array.length; i) {const secondElement parseInt(array[i][1]);if (secondElement > max) {max secondElement;}}return max;}, 获取最大值索引&…

数据结构——(单)链表

文章目录 1. 结构 2. 链表的分类 1. 单链表 2. 双链表 3. 循环单链表 4. 循环双链表 3. 优缺点 4. 单链表函数 5. 单链表代码实现 1. 结构 逻辑结构 链表是一种线性结构&#xff0c;由一系列结点&#xff08;Node&#xff09;组成。每个结点包含一个数据元素和一个指…

Linux库概念及相关编程(动态库-静态库)

Linux库概念及相关编程 分文件编程案例 分文件编程是指将程序按功能模块划分成不同的文件进行编写&#xff0c;这种方法有以下好处&#xff1a; 功能责任划分&#xff1a;每个文件对应一个功能模块&#xff0c;职责明确&#xff0c;易于理解和维护。方便调试&#xff1a;可以…

绝地求生PUBG点击开始游戏一直在加载不读条计时间的解决办法

绝地求生PUBG作为一款引领潮流的大逃杀游戏&#xff0c;凭借其紧张刺激的对抗体验赢得了全球玩家的喜爱。 即使是游戏已经上线很长时间了&#xff0c;但是游戏现在依旧是很火爆&#xff0c;还有很多玩家下载游戏进行游玩。然而&#xff0c;一些为玩家在游戏中遇到了点击开始游戏…

java版本ERP管理系统源码 Spring Cloud ERP_ERP系统_erp软件_ERP管理系统

在当今数字化时代&#xff0c;企业对高效、稳定且易于扩展的管理系统的需求日益增长。为了满足这一需求&#xff0c;我们精心打造了一款基于Java技术的ERP&#xff08;Enterprise Resource Planning&#xff09;管理系统。该系统充分利用了Spring Cloud Alibaba、Spring Boot、…

Butterfly主题文章标题改成转动小风车

效果 标题级别不同小风车颜色不同&#xff0c;鼠标移入会有转动变慢及变色效果。 新建css 建议在/source下创建诸如img/css/js等文件夹&#xff0c;存放文章或网站用的素材&#xff0c;分门别类后续也方便维护。 Hexo打包的时候&#xff0c;会自动把/source下的文件&#…

JavaScript基础知识5(对象)

JavaScript基础知识5&#xff08;对象&#xff09; 对象创建对象使用对象字面量使用 new Object() 访问和修改属性点表示法方括号表示法 动态添加和删除属性添加属性删除属性 对象方法对象的遍历常用属性和方法数学常量数学函数三角函数 使用示例生成随机整数计算圆的面积求最大…

Zabbix 配置 VMware 监控

Zabbix监控VMware 官方文档&#xff1a;https://www.zabbix.com/documentation/current/en/manual/vm_monitoring Zabbix 可以使用低级发现规则自动发现 VMware 虚拟机管理程序和虚拟机&#xff0c;并根据预定义的主机原型创建主机来监控它们。Zabbix 还包括用于监控 VMware …

VirtualBox的windows server 2016设置主机和虚拟机共享文件夹

文章目录 安装步骤1. windows server 2016安装增强功能2.上述安装完成之后&#xff0c;需要做共享文件夹&#xff0c;在宿主机&#xff0c;新建一个test文件夹&#xff0c;做共享设置&#xff0c;如下图&#xff1a;3.然后打开虚拟机&#xff0c;设置文件共享 安装步骤 1. win…

字节码编程javassist之定义方法和返回值

写在前面 源码 。 本文看下如何使用javassist来定义方法和返回值。 1&#xff1a;源码 package com.dahuyou.javassist.generateFieldAndMethod;import javassist.*;import java.lang.reflect.Method;public class JustDoIt222 {public static void main(String[] args) thr…

跨平台Ribbon UI组件QtitanRibbon全新发布v6.7.0——支持Qt 6.6.3

没有Microsoft在其办公解决方案中提供的界面&#xff0c;就无法想象现代应用程序&#xff0c;这个概念称为Ribbon UI&#xff0c;目前它是使应用程序与时俱进的主要属性。QtitanRibbon是一款遵循Microsoft Ribbon UI Paradigm for Qt技术的Ribbon UI组件&#xff0c;QtitanRibb…

SOLIDWORKS分期许可(订阅形式),降低前期的投入成本!

SOLIDWORKS 分期许可使您能够降低前期软件成本&#xff0c;同时提供对 SOLIDWORKS 新版本和升级程序的即时访问&#xff0c;以及在每个期限结束时调整产品的灵活性&#xff0c;帮助您跟上市场需求和竞争压力的步伐。 目 录&#xff1a; ★ 1 什么是SOLIDWORKS分期许可 ★ 2 …