嘟嘟程序猿

SpringBoot--开发实战--SpringSession会

2019-08-08  本文已影响36人  无剑_君

一、Spring-session简介

  在传统单机web应用中,一般使用tomcat/jetty等web容器时,用户的session都是由容器管理。浏览器使用cookie中记录sessionId,容器根据sessionId判断用户是否存在会话session。这里的限制是,session存储在web容器中,被单台服务器容器管理。
  但是网站主键演变,分布式应用和集群是趋势(提高性能)。此时用户的请求可能被负载分发至不同的服务器,此时传统的web容器管理用户会话session的方式即行不通。除非集群或者分布式web应用能够共享session,尽管tomcat等支持这样做。但是这样存在以下两点问题:

  1. 需要侵入web容器,提高问题的复杂;
  2. web容器之间共享session,集群机器之间势必要交互耦合;
    基于这些,必须提供新的可靠的集群分布式/集群session的解决方案,突破traditional-session单机限制(即web容器session方式,下面简称traditional-session),spring-session应用而生。
    传统模式
      传统模式中,当request进入web容器,根据reqest获取session时,如果web容器中存在session则返回,如果不存在,web容器则创建一个session。然后返回response时,将sessonId作为response的head一并返回给客户端或者浏览器。
    spring-session模式
      spring-session的核心思想在于此:将session从web容器中剥离,存储在独立的存储服务器中。目前支持多种形式的session存储器:Redis、Database、MogonDB等。session的管理责任委托给spring-session承担。当request进入web容器,根据request获取session时,由spring-session负责存存储器中获取session,如果存在则返回,如果不存在则创建并持久化至存储器中。
    官网:https://spring.io/projects/spring-session
    用nginx做负载的时候,会出现每一次http请求都会分配到不同的tomcat上,这样用session存储用户数据就会有问题。因为tomcat集群之间没有实现session共享。

二、spring-session实现方案

  1. Servlet获取Session的方式
HttpServletRequest request = ...
HttpSession session = request.getSession(false);

其中HttpServletRequest和HttpSession都是servlet规范中定义的接口,web容器实现的标准。那如果引入》> spring-session,要如何获取session?
1)遵循servlet规范,同样方式获取session,对应用代码无侵入且对于developers透明化
2)全新实现一套session规范,定义一套新的api和session管理机制

两种方案都可以实现,但是显然第一种更友好,且具有兼容性。spring-session正是第一种方案的实现。
实现第一种方案的关键点在于做到透明和兼容;
接口适配:仍然使用HttpServletRequest获取session,获取到的session仍然是HttpSession类型——适配器模式;
类型包装增强:Session不能存储在web容器内,要外化存储——装饰模式;

三、特点与工作原理

spring-session在无需绑定web容器的情况下提供对集群session的支持。并提供对以下情况的透明集成:

HttpSession:容许替换web容器的HttpSession
WebSocket:使用WebSocket通信时,提供Session的活跃
WebSession:容许以应用中立的方式替换webflux的webSession

工作原理

spring-session分为以下核心模块:

SessionRepositoryFilter:Servlet规范中Filter的实现,用来切换HttpSession至Spring Session,包装HttpServletRequest和HttpServletResponse
HttpServerletRequest/HttpServletResponse/HttpSessionWrapper包装器:包装原有的HttpServletRequest、HttpServletResponse和Spring Session,实现切换Session和透明继承HttpSession的关键之所在
Session:Spring Session模块
SessionRepository:管理Spring Session的模块
HttpSessionStrategy:映射HttpRequst和HttpResponse到Session的策略

四、Maven依赖

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-core</artifactId>
</dependency>
或
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis 依赖-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.0</version>
</dependency>

五、application.properties配置

#session
#配合为redis表示开启redis会话共享,none单机
# spirngboot默认就是使用redis方式,也可以写redis
spring.session.store-type=none
spring.session.redis.namespace=evidence

#redis配置
spring.data.redis.repositories.enabled=true
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.lettuce.pool.max-active=1024
spring.redis.lettuce.pool.min-idle=10

Spring Boot配置很容易切换到不同的Session管理方式,总共有以下几种:
Redis,Session数据存放Redis中。
JDBC,会话数据存放在数据库中,默认情况下SPRINGSESSION表存放Session基本信息,如sessionId、创建时间、最后一次访问时间等,SPRING_SESSION ATTRIBUTES存放了session数据,ATTRIBUTE_NAME列保存了Session的Key,ATTRIBUTE_BYTES列以字节形式保存了Session的Value,Spring Session会自动创建这两张表。
Hazelcast,Session数据存放到Hazelcast。
None,禁用Spring Session功能。

六、创建控制器

@Controller
@RequestMapping(value = "/")
public class IndexController {
    @ResponseBody
    @GetMapping(value = "/session")
    public Map<String, Object> getSession(HttpServletRequest request) {
        // 添加数据到Session
        request.getSession().setAttribute("username", "admin");
        // 添加sessionID到Map
        Map<String, Object> map = new HashMap<>();
        map.put("sessionId", request.getSession().getId());
        return map;
    }

    @ResponseBody
    @GetMapping(value = "/get")
    public String get(HttpServletRequest request) {
        // 获取Session数据
        String userName = (String) request.getSession().getAttribute("username");
        return userName;
    }

    @ResponseBody
    @GetMapping(value = "/logout")
    public String logout(HttpServletRequest request) {
        // 销毁sessioin
        request.getSession().invalidate();
        return "ok";
    }
}

七、启动类配置

@SpringBootApplication
@EnableRedisHttpSession     // 启用ResisSession存储
@EnableCaching              // 启用缓存
public class SpringSessionApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringSessionApplication.class, args);
    }
}

八、测试

创建sessioin数据 获取session数据 redis查看

九、Session监听器

  1. 创建监听配置类
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 120)
@Slf4j
public class SpringSessionConfiguration {
    /**
     * Redis内session过期事件监听
     * @param expiredEvent
     */
    @EventListener
    public void onSessionExpired(SessionExpiredEvent expiredEvent) {
        String sessionId = expiredEvent.getSessionId();
        log.info("Session失效事件:"+sessionId);

    }

    /**
     * Redis内session创建事件监听
     * @param createdEvent
     */
    @EventListener
    public void onSessionCreated(SessionCreatedEvent createdEvent) {
        String sessionId = createdEvent.getSessionId();
        log.info("创建Session事件:"+sessionId);
    }

    /**
     * Redis内session删除事件监听
     * @param deletedEvent
     */
    @EventListener
    public void onSessionDeleted(SessionDeletedEvent deletedEvent) {
        String sessionId = deletedEvent.getSessionId();
        log.info("删除Session事件:"+sessionId);
    }
}

  1. 测试结果
    请求地址分别为:
    http://localhost:9000/session
    http://localhost:9000/logout
    测试结果

十、常见问题

  1. 使用@WebListener不能监听?
    正常,经测试,如果使用SpringSessioin必须使用配置方式。
上一篇 下一篇

猜你喜欢

热点阅读