栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

分布式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、依赖导入与环境配置


    org.springframework.session
    spring-session-data-redis


    org.springframework.boot
    spring-boot-starter-data-redis


在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 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() {});
        // 放入session  // key为loginUser
        session.setAttribute(AuthServerConstant.LOGIN_USER, respVo);//loginUser
        log.info("n欢迎 [" + respVo.getUsername() + "] 登录");
        // 登录成功重定向到首页
        return "redirect:http://mall.com";
    }else {
        HashMap error = new HashMap<>();
        // 获取错误信息
        error.put("msg", r.getData("msg",new TypeReference(){}));
        redirectAttributes.addFlashAttribute("errors", error);
        return "redirect:http://auth.mall.com/login.html";
    }
}

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号