目录
  • session
  • session常用解决方案
  • spring-session
    • 基于数据库的spring-session
    • 基于redis的spring-session

    session

    http协议是无状态的,这样对于服务端来说,没有办法区分是新的访客还是旧的访客。但是,有些业务场景,需要追踪用户多个请求,此时就需要session。关于session的百度百科session

    session:在计算机中,尤其是在网络应用中,称为“会话控制”。session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的web页之间跳转时,存储在session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 web页时,如果该用户还没有会话,则web服务器将自动创建一个 session对象。当会话过期或被放弃后,服务器将终止该会话

    核心特点:

    1. 服务端存储
    2. 会过期

    session常用解决方案

    对于session的常用解决方案,可以划分为三种。

    • 负载均衡方式

    借助负载均衡设备或者模块,将指定的session始终路由到同一台机器即可,如nginx。

    • 副本复制方式

    利用服务器节点间的副本复制方式,保证集群所有节点拥有的session数据一致。

    • 集中存储方式

    引入第三方存储,将session数据集中存储到外部存储中,如redis或者数据库等。

    本文介绍的spring-session是采用第三种,集中存储的方式。

    spring-session

    核心组成模块

    • spring session core

    提供spring session核心的功能和api

    • spring session data redis

    提供基于redis的sessionrepository以及配置

    • spring session jdbc

    提供基于关系型数据库的sessionrepository以及配置

    • spring session hazelcast

    提供基于hazelcast的sessionrepository以及配置

    测试代码

    controller提供三个接口,分别对应session的获取、保存和清理

    @getmapping("/")
    public string process(model model, httpsession session) {
      @suppresswarnings("unchecked")
      list<string> messages = (list<string>) session.getattribute("springsession");
    
      if (messages == null) {
        messages = new arraylist<>();
      }
      model.addattribute("sessionmessages", messages);
    
      return "sessiontest";
    }
    
    @postmapping("/persistsession")
    public string persistmessage(@requestparam("msg") string msg, httpservletrequest request) {
      @suppresswarnings("unchecked")
      list<string> messages = (list<string>) request.getsession().getattribute("springsession");
      if (messages == null) {
        messages = new arraylist<>();
        request.getsession().setattribute("springsession", messages);
      }
      messages.add(msg);
      request.getsession().setattribute("springsession", messages);
      return "redirect:/";
    }
    
    @postmapping("/destroysession")
    public string destroysession(httpservletrequest request) {
      request.getsession().invalidate();
      return "redirect:/";
    }
    

    sessiontest.html对应页面操作

    <!doctype html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="utf-8">
        <title>spring boot session example</title>
    </head>
    <body>
    <div>
        <form th:action="@{/persistsession}" method="post">
            <textarea name="msg" cols="40" rows="2"></textarea>
            <br> <input type="submit" value="保存" />
        </form>
    </div>
    <div>
        <h2>session列表</h2>
        <ul th:each="message : ${sessionmessages}">
            <li th:text="${message}">message</li>
        </ul>
    </div>
    
    <div>
        <form th:action="@{/destroysession}" method="post">
            <input type="submit" value="清空" />
        </form>
    </div>
    </body>
    </html>
    

    基于数据库的spring-session

    1.引入maven依赖

    使用mysql存储,所以引入了mysql。

    涉及到springboot jdbc的配置,引入了spring boot jdbc starter。

    <dependency>
      <groupid>org.springframework.session</groupid>
      <artifactid>spring-session-core</artifactid>
      <version>2.5.0</version>
    </dependency>
    
    <dependency>
      <groupid>org.springframework.session</groupid>
      <artifactid>spring-session-jdbc</artifactid>
      <version>2.5.0</version>
    </dependency>
    
    <dependency>
      <groupid>mysql</groupid>
      <artifactid>mysql-connector-java</artifactid>
      <version>8.0.18</version>
    </dependency>
    
    <dependency>
      <groupid>org.springframework.boot</groupid>
      <artifactid>spring-boot-starter-jdbc</artifactid>
    </dependency>
    

    注意:

    no session repository could be auto-configured, check your configuration (session store type is ‘jdbc’)

    如果存在这个报错,是因为没有引入spring-boot-starter-jdbc,引入即可。

    2.配置application.properties文件

    主要包含两部分,数据库的配置以及spring session jdbc配置。

    # 配置数据源相关内容
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.driver
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_learn?createdatabaseifnotexist=true&autoreconnect=true&usessl=false
    spring.datasource.username=xxx
    spring.datasource.password=xxx
    spring.datasource.initialization-mode=always
    
    # session类型选择jdbc
    spring.session.store-type=jdbc
    spring.session.jdbc.initialize-schema=always
    # 指定表名
    #spring.session.jdbc.table-name=sessions
    # 超时时间
    spring.session.timeout=180s
    

    3.数据库存储解析

    默认情况下,数据库中会创建2张表。spring_session和spring_session_attribution。

    spring_session用于存在session自身的一些属性,如创建时间、过期时间等,详细schema如下。

    create table `spring_session` (
      `primary_id` char(36) not null,
      `session_id` char(36) not null,
      `creation_time` bigint not null,
      `last_access_time` bigint not null,
      `max_inactive_interval` int not null,
      `expiry_time` bigint not null,
      `principal_name` varchar(100) default null,
      primary key (`primary_id`),
      unique key `spring_session_ix1` (`session_id`),
      key `spring_session_ix2` (`expiry_time`),
      key `spring_session_ix3` (`principal_name`)
    ) engine=innodb default charset=utf8mb4 collate=utf8mb4_0900_ai_ci row_format=dynamic
    

    spring_session_attribution用于存储session相关联的属性,schema如下。

    create table `spring_session_attributes` (
      `session_primary_id` char(36) not null,
      `attribute_name` varchar(200) not null,
      `attribute_bytes` blob not null,
      primary key (`session_primary_id`,`attribute_name`),
      constraint `spring_session_attributes_fk` foreign key (`session_primary_id`) references `spring_session` (`primary_id`) on delete cascade
    ) engine=innodb default charset=utf8mb4 collate=utf8mb4_0900_ai_ci row_format=dynamic
    

    4.测试执行

    spring_session中的数据

    spring_session_attribution中的数据。

    基于redis的spring-session

    几乎同样的步骤

    maven依赖

    <dependency>
      <groupid>org.springframework.boot</groupid>
      <artifactid>spring-boot-starter-data-redis</artifactid>
    </dependency>
    
    <dependency>
      <groupid>org.springframework.session</groupid>
      <artifactid>spring-session-data-redis</artifactid>
    </dependency>
    

    application.properties配置

    spring.session.store-type=redis
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    

    结果分析

    一次请求后,多了三个属性,分析如下。

    key 类型 用途 value
    spring:session:sessions:expires:${sessionid} string 判断sesssion是否存在
    spring:session:sessions:${sessionid} hash session相关的属性,包括有效期、创建时间、具体属性等 creationtime/lastaccessedtime/sessionattr/maxinactiveinterval
    spring:session:expirations:1623656160000 set 存储待过期的sessionid列表 key: 过期的时间戳;value: 在这个时间戳将要过期的expire key列表。

    在访问时,先通过第一个key,判断session是否存在以及是否过期。如果没有过期,可以通过第二个key获取或者更新对应的session详情。

    对于第三个key,实际上spring-session-redis会有特殊的用途,主要是为了redis的keyspace-notificationhttps://redis.io/topics/notifications。核心目的是为了确保过期的session一定要触发过期事件。关于这方面的解释,可以看一下redisindexedsessionrepository中的注释。

    订阅spring-session的相关事件

    有些时候,我们比较关心session的创建或者销毁事件,做一些特殊的处理或者记录。基于redis的spring-session利用spring event将该事件发布出来,我们可以使用eventlistener监听做处理。

    @component
    @slf4j
    public class annotationdriveneventlistener {
    
      @eventlistener
      public void handlesessioncreated(sessioncreatedevent sessioncreatedevent) {
        string sessionid = sessioncreatedevent.getsessionid();
        log.info("session id:{} created", sessionid);
      }
    
      @eventlistener
      public void handlesessiondestroyed(sessiondestroyedevent sessiondestroyedevent) {
        string sessionid = sessiondestroyedevent.getsessionid();
        log.info("session id:{} destroyed", sessionid);
      }
    }
    

    总结

    spring session提供了非常便利的,基于关系型数据库或者redis的session解决方案。

    redis版访问速度快,基于redis的过期策略,保证过期数据会被删除,同时支持事件订阅。

    数据库版直接基于数据库,无需单独引入其他存储。但是访问速度相对较慢,过期数据需要依赖应用程序自身进行删除。同时没有提供事件订阅能力。

    以上就是spring session的使用示例的详细内容,更多关于spring session的使用的资料请关注www.887551.com其它相关文章!