分布式Session分析与代码实战(SpringSession)

一、介绍

1、什么是session? cookie?

session对象存储在服务器端内存中,cookie存储在客户端浏览器中。一般是客户端请求服务器,服务器端生成session对象,将session对象存储在本后端服务的内存中,并且在响应头中放入sessionId响应给客户端,命令客户端保存sessionid到cookie。当浏览器以后发请求时会自动将本地cookie中存储的seesionId通过请求头的方式传递给服务器,这样服务器和客户端就能保持会话信息。

2、什么是分布式session,为什么需要分布式session?

  1. 为了提高服务器端的负载能力,后台一般将服务器节点做集群,通过ngnix通过轮询的方式转发到目标服务器。例如:当浏览器首次访问A服务器做登录,生成session对象。然后第二次请求在访问后台接口,如果正好被ngnix转发到了A服务器,那么没问题可以获取到session对象。如果请求被转发到B服务器,由于之前生成的session对象保存在A服务器中了,B服务器根本没有那个session对象,就会认定为未登录等情况。
  2. session不可跨域,它有自己的作用范围,当我们有跨域的需求时也需要用分布式session。例如:做购物网站时,点击登录,跳转到auth.mail.com域名的认证微服务中进行登录,但是访问mail.com域名下其他服务时,拿不到session,被判定为未登录。

因此,所谓分布式session就是要session要能在不同服务和同服务的集群的共享

二、解决方案

1、session复制(不推荐)

用户登录后得到session后,服务把session也复制到别的机器上,显然这种处理很不好,这里只简单介绍一下这种方案。
在这里插入图片描述

2、hash一致性(不推荐)

根据用户计算hash值,路由到指定的机器上登录。每个用户每次都会被路由到同一个后台节点,但是远程调用还是不好解决。
在这里插入图片描述

3、springsession整合redis统一存储(推荐)

最终方案,整合springsession,把session放到redis中,后续代码都是基于这个实现的。

在这里插入图片描述

三、代码实现

1、先附上官方文档:
https://spring.io/projects/spring-session-data-redis
https://docs.spring.io/spring-session/docs/2.4.2/reference/html5/#modules
核心思想是通过SpringSession修改session的作用域。

2、依赖导入与环境配置

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

在properties中添加配置信息。

spring.session.store-type=redis
server.servlet.session.timeout=30m
spring.redis.host=192.168.56.10

在启动类头上添加注解,用于开启springsession。

@EnableRedisHttpSession //创建了一个springSessionRepositoryFilter ,负责将原生HttpSession 替换为Spring Session的实现

3、扩大session作用域与设置session序列化

由于默认使用jdk进行序列化,通过导入RedisSerializer修改为json序列化。并且通过修改CookieSerializer扩大session的作用域至**.mall.com。

编写配置类如下:

@Configuration
public class MallSessionConfig {

    @Bean // redis的json序列化
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }

    @Bean // cookie
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("MAILSESSIONID"); // cookie的键
        serializer.setDomainName("mall.com"); // 扩大session作用域,也就是cookie的有效域
        return serializer;
    }
}

注意,这个配置需要放到每个需要共享session的微服务下(不管是读取还是写入),并不是只是放到写入session的微服务下。

至此,分布式session就保存好了。

4、补充:分布式session的取用例子

假设做个登录验证的Controller:

@GetMapping({"/login.html","/","/index","/index.html"}) // auth服务
public String loginPage(HttpSession session){
    // 从会话从获取loginUser
    Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);// "loginUser";
    System.out.println("attribute:"+attribute);
    if(attribute == null){
        return "login";
    }
    System.out.println("已登陆过,重定向到首页");
    return "redirect:http://mall.com";
}


@PostMapping("/login") // auth服务
public String login(UserLoginVo userLoginVo,
                    RedirectAttributes redirectAttributes,
                    HttpSession session){
    // 调用远程登录接口,验证账号密码是否正确
    R r = memberFeignService.login(userLoginVo);
    if(r.getCode() == 0){
        // 登录成功
        MemberRespVo respVo = r.getData("data", new TypeReference<MemberRespVo>() {});
        // 放入session  // key为loginUser
        session.setAttribute(AuthServerConstant.LOGIN_USER, respVo);//loginUser
        log.info("\n欢迎 [" + respVo.getUsername() + "] 登录");
        // 登录成功重定向到首页
        return "redirect:http://mall.com";
    }else {
        HashMap<String, String> error = new HashMap<>();
        // 获取错误信息
        error.put("msg", r.getData("msg",new TypeReference<String>(){}));
        redirectAttributes.addFlashAttribute("errors", error);
        return "redirect:http://auth.mall.com/login.html";
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值