SpringBoot 实现Spring session共享

session共享是什么?

1)HttpSession是通过Servlet容器进行创建和管理的。在单服务环境中,通过Http请求创建的Session信息是存储在Web服务器内存中的,如Tomcat、Jetty等。
2)现在很多的服务器都采用分布式集群的方式进行部署,用户在发起第一次请求时候访问了A站点,并在A站点的session中保存了登录信息,
当用户第二次发起请求,通过负载均衡请求分配到B站点了,那么此时B站点能否获取用户保存的登录的信息呢?答案是不能的,因为Session是存储在对应Web服务器的内存中的,不能进行共享,可以用Spring-sessio解决session的共享问题。

session共享能解决什么具体问题?

在分布式结构的系统中,每台服务器之间有着不可共享数据的鸿沟,比如用户登录信息或其他一次请求需要在多个服务器间随机使用的情况下,可使用session共享

用什么中间件来实现session共享?

当所有的tomcat(即多个服务器)都往session中写数据时,就统一往redis中写数据,同理所有tomcat读数据就去redis中读,这样就可以做到session共享了,不同的服务就可以使用相同的 Session 数据了,这样的方案,可以由开发者手动实现,即手动往 Redis 中存储数据,手动从 Redis 中读取数据,相当于使用一些 Redis 客户端工具来实现这样的功能,毫无疑问,手动实现工作量还是蛮大的。

一个简化的方案就是使用 Spring Session 来实现这一功能,
Spring Session 就是使用 Spring 中的代理过滤器,将所有的 Session 操作拦截下来,自动的将数据 同步到 Redis 中,或者自动的从 Redis 中读取数据。

对于开发者来说,所有关于 Session 同步的操作都是透明的,开发者使用 Spring Session,一旦配置完成后,具体的用法就像使用一个普通的 Session 一样。

Redis存储session的需要考虑问题?

  1. session数据如何在Redis中存储?
  2. session属性变更何时触发存储?
    我们的实现:
    考虑到session中数据类似map的结构,采用redis中hash存储session数据比较合适,如果使用单个value存储session数据,不加锁的情况下,就会存在session覆盖的问题,因此使用hash存储session,
    每次只保存本次变更session属性的数据,避免了锁处理,性能更好。

如果每改一个session的属性就触发存储,在变更较多session属性时会触发多次redis写操作,对性能也会有影响,我们是在每次请求处理完后,做一次session的写入,并且之写入变更过的属性。

如果本次没有做session的更改, 是不会做redis写入的,仅当没有变更的session超过一个时间阀值(不变更session刷新过期时间的阀值),就会触发session保存,以便session能够延长有效期

sptingboot 实现session共享的步骤

引入依赖

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

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

 <dependency>
  <groupId>org.springframework.boot</groupId> 
   <artifactId>spring-boot-starter-test</artifactId>    
 <scope>test</scope>
 </dependency>

 <dependency>  
 <groupId>org.springframework.security</groupId>    
 <artifactId>spring-security-core</artifactId>   
 <version>5.1.6.RELEASE</version> 
</dependency>

配置文件

#Tomcat端口 不同服务的端口不同
server:
  port: 8080

#Redis
#Redis数据库索引(默认为0)
  redis:
    #数据库名
    database: 0
  # Redis服务器地址
    host: localhost
  # Redis服务器连接端口
    port: 6379
  # Redis服务器连接密码(默认为空)
    password:
  # 连接池最大连接数(使用负值表示没有限制) 默认 8
    lettuce:
      pool:
        max-active: 8
  # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-wait: -1
  # 连接池中的最大空闲连接 默认 8
        max-idle: 8
  # 连接池中的最小空闲连接 默认 0
        min-idle: 0

因为Redis作为session数据保存的中间件,所以需要先配置redis,更多的rdis操作可参考我的另一篇博客 https://blog.csdn.net/weixin_43784880/article/details/104321420

RedisSession配置类

import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

@Configuration
//maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 属性不再生效。
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class SessionConfig {
}

maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 属性不再生效。

Redis启动配置类

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}

@EnableCaching开启redis

Web类

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.UUID;
@RestController
@RequestMapping("/test")
public class TestController {

    @Value("${server.port}")
    Integer port;

    @RequestMapping(value = "/set", method = RequestMethod.POST)
    public String set(HttpSession session) {
        session.setAttribute("session2", "guangzhou");
        return String.valueOf(port) + session.getId();
    }

    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public String get(HttpSession session) {
        return session.getAttribute("session2") + ":" + port;
    }

}

为了获取每一个请求到底是哪一个 服务器 提供的服务(负载均衡随机分配),需要在每次请求时返回当前服务的端口号,因此这里我注入了 server.port

此处您可以为了方便测试,如果没有现成的分布式架构的项目,可按照上方步骤配置两个一模一样的springboot项目
除了properties.yml的server.port 即tomcat的服务端口不同之外,其他几乎一致,比如配置两个项目,分别为server.port=8080、server.port=8081
分别测试web类中get和set请求

注:测试前要先启动redis 如何启动自行百度

工具postman或浏览器实现 Post请求 http://127.0.0.1:8080/test/set
在这里插入图片描述返回值为请求到的服务器端口+sessionid

Get请求 http://127.0.0.1:8081/test/get
在这里插入图片描述返回值为set方法保存的session的value值

这时候测试通过后如何通过redis查看我们保存的sessio数据呢?

通过命令在redis根目录打开客户端查看session的保存情况 redis.cli -p port 此处我的是默认端口6379
使用keys ‘session’ 查看redis中所有的key带session数据 此处*表示模糊查询 查处的的数据是key带有session字眼的数据
也可以使用keys * 查出所有的缓存数据 为了避免结果返回过多难以查阅 所以使用keys ‘session查找
keys ‘sesion
在这里插入图片描述
keys *
在这里插入图片描述

查出来后可以看到红圈处的内容与我们get查找出来的内容一致,说明我们session不仅可以在多个服务器间共享,同时可以再redis中看到

使用spring session+redis存储的session如何查看.

127.0.0.1:6379> keys '*session*'
1) "spring:session:sessions:ba9c52ac-96a1-4252-ab6e-c2a2f489419f"
2) "spring:session:sessions:expires:ba9c52ac-96a1-4252-ab6e-c2a2f489419f"
3) "spring:session:expirations:1584436920000"
4) "spring:session:expirations:1584341460000"
5) "spring:session:sessions:16829b83-860f-4841-8943-8ce6d3d8886e"
6) "spring:session:sessions:fce83f11-1012-4bbf-866e-d0693a81bc37"
7) "spring:session:sessions:expires:fce83f11-1012-4bbf-866e-d0693a81bc37"
8) "spring:session:sessions:expires:16829b83-860f-4841-8943-8ce6d3d8886e"
9) "spring:session:expirations:1584286860000"
127.0.0.1:6379> type spring:session:sessions:fce83f11-1012-4bbf-866e-d0693a81bc37
hash
      127.0.0.1:6379> hkeys spring:session:sessions:fce83f11-1012-4bbf-866e-d0693a81bc37
1) "sessionAttr:session1"
2) "sessionAttr:uid"
3) "sessionAttr:session2"
4) "creationTime"
5) "maxInactiveInterval"
6) "lastAccessedTime"

在这里插入图片描述
在这里插入图片描述
上图即我们java存储的session数据 session1和uid 是我之前测试的 不必理睬

存储在redis中的key的简单介绍说明:
type 查看数据类型

//存储 Session 数据,数据类型hash,可以使用type查看
Key:spring:session:sessions:eeefefeae-c8ea-4346-ba6b-9b3b26eee578  
使用
type  spring:session:sessions:eeefefeae-c8ea-4346-ba6b-9b3b26eee578 返回的是hash   也是我们session保存的位置,session实质就是key-value 
eeefefeae-c8ea-4346-ba6b-9b3b26eee578 为 sessionId

//Redis TTL触发Session 过期。(Redis 本身功能),数据类型:String
Key:spring:session:sessions:expires:133337740000
使用
type spring:session:sessions:expires:133337740000    返回时String   表示的是该session缓存的有效时间

//执行 TTL key ,可以查看剩余生存时间
//定时Job程序触发Session 过期。(spring-session 功能),数据类型:Set
Key:spring:session:expirations:133337740000 
使用
type  spring:session:expirations:133337740000  返回的是set集合的数据类型   
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值