Application of distributed session-SpringSession

I am participating in the "Nuggets·Starting Plan"

springsession

Spring-Session official documentation: springsession

The characteristics of spring session

Spring Session provides a set of solutions for creating and managing Servlet HttpSession. By default, external Redis is used to store Session data to solve the problem of Session sharing.

Spring Session provides the following features:

  • API and implementation for managing user sessions;
  • Allows replacement of HttpSession in an application container (i.e. Tomcat) neutral way;
  • Spring Session makes supporting cluster sessions less cumbersome
  • Spring session supports managing sessions of multiple users in a single browser instance.
  • Spring Session allows to provide session ID in headers to use RESTful API.

Realization principle

The implementation of Spring-Session is to design a filter SessionRepositoryFilter , SessionRepositoryFilter will first intercept the request, and convert the request and response objects into SessionRepositoryRequestWrapper and SessionRepositoryResponseWrapper. Later, when the getSession method of request is called for the first time, the getSession method of SessionRepositoryRequestWrapper will be called.

If you can't find the session from the attributes in the request, then get the sessionid through the cookie to find it in redis. If you can't find it, create a redissession object directly and synchronize it to redis. Transfer the process of creating and destroying sessions from the server to redis.

Part of the source code

/**
     * HttpServletRequest getSession()实现
     */
    @Override
    public HttpSessionWrapper getSession() {
        return getSession(true);
    }

    @Override
    public HttpSessionWrapper getSession(boolean create) {
        HttpSessionWrapper currentSession = getCurrentSession();
        if (currentSession != null) {
            return currentSession;
        }
        //从当前请求获取sessionId
        String requestedSessionId = getRequestedSessionId();
        if (requestedSessionId != null
                && getAttribute(INVALID_SESSION_ID_ATTR) == null) {
            S session = getSession(requestedSessionId);
            if (session != null) {
                this.requestedSessionIdValid = true;
                currentSession = new HttpSessionWrapper(session, getServletContext());
                currentSession.setNew(false);
                setCurrentSession(currentSession);
                return currentSession;
            }
            else {
                // This is an invalid session id. No need to ask again if
                // request.getSession is invoked for the duration of this request
                if (SESSION_LOGGER.isDebugEnabled()) {
                    SESSION_LOGGER.debug(
                            "No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
                }
                setAttribute(INVALID_SESSION_ID_ATTR, "true");
            }
        }
        if (!create) {
            return null;
        }
        if (SESSION_LOGGER.isDebugEnabled()) {
            SESSION_LOGGER.debug(
                    "A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "
                            + SESSION_LOGGER_NAME,
                    new RuntimeException(
                            "For debugging purposes only (not an error)"));
        }
        //为当前请求创建session
        S session = SessionRepositoryFilter.this.sessionRepository.createSession();
        //更新时间
        session.setLastAccessedTime(System.currentTimeMillis());
        //对Spring session 进行包装(包装成HttpSession)
        currentSession = new HttpSessionWrapper(session, getServletContext());
        setCurrentSession(currentSession);
        return currentSession;
    }

    /**
     * 根据sessionId获取session
     */
    private S getSession(String sessionId) {
        S session = SessionRepositoryFilter.this.sessionRepository
                .getSession(sessionId);
        if (session == null) {
            return null;
        }
        session.setLastAccessedTime(System.currentTimeMillis());
        return session;
    }

    /**
     * 从当前请求获取sessionId
     */
    @Override
    public String getRequestedSessionId() {
        return SessionRepositoryFilter.this.httpSessionStrategy
                .getRequestedSessionId(this);
    }

    private void setCurrentSession(HttpSessionWrapper currentSession) {
        if (currentSession == null) {
            removeAttribute(CURRENT_SESSION_ATTR);
        }
        else {
            setAttribute(CURRENT_SESSION_ATTR, currentSession);
        }
    }
    /**
     * 获取当前请求session
     */
    @SuppressWarnings("unchecked")
    private HttpSessionWrapper getCurrentSession() {
        return (HttpSessionWrapper) getAttribute(CURRENT_SESSION_ATTR);
    }

Combined with the use of SpringBoot

1. Import maven dependencies

  <!--依赖 data-redis的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--不能忘记这个依赖-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!--添加cache的依赖信息-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!--添加 session的依赖-->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

        <!--解决spring-session处理缓存时乱码的问题-->

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.69</version>
        </dependency>

2. Write configuration class

Here we use redis to store session data to achieve session sharing

spring:
  # 配置Redis的使用
  redis:
    database: 1 # 所使用的数据库  默认是0
    host: localhost  #所使用的redis的主机地址
    port: 7000  # 端口号  默认是 6379
    password:  # 密码
    timeout: 5000 # 超时时间  5000毫秒
    # 连接池 lettuce 的配置
    lettuce:
      pool:
        max-active: 100
        min-idle: 10
        max-wait: 100000
  # 配置session的相关信息
  session:
    store-type: redis  # 配置存储的类型
    timeout: 3600  # 配置过期时间
    redis:
      flush-mode: on_save # 保存时刷新
      namespace: springSession # 命令空间
server:
  port: 8081
  servlet:
    context-path: /session

3. Write the controller to call

@RestController
public class SessionController {
    @Value("${server.port}")
    private String port;

    @RequestMapping("/createSession")
    public String createSession(HttpSession httpSession){
        String sessionId=httpSession.getId();
        httpSession.setAttribute("name",port+sessionId);
        httpSession.setAttribute("sname",port+":abc");
        return sessionId+"创建端口号是:"+port+"的应用创建Session,属性是:"+httpSession.getAttribute("name").toString();
    }

    @RequestMapping("/getSession")
    public String getSession(HttpSession httpSession){
        return "访问端口号是:"+port+",获取Session属性是:"+httpSession.getAttribute("name").toString();
    }
}

4. Solve the problem of session storing garbled characters in redis

Fastjson has been imported before, plus this configuration class

@Configuration
@EnableRedisHttpSession
public class RedisSessionConfig {

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        // 使用 FastJsonRedisSerializer 来序列化和反序列化redis 的 value的值
        FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class);
        ParserConfig.getGlobalInstance().addAccept("com.muzz");
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setCharset(StandardCharsets.UTF_8);
        serializer.setFastJsonConfig(fastJsonConfig);
        return serializer;
    }
}

5. Package to view the effect

To use the package in the maven tool, you need to start two ports to simulate the effect. First, start a creation session running resultto enter the location where the jar package is packaged, and enter the cmd command line

java -jar xxx.jar --server.port=8081

Start another port to access the following is the session data stored by redisinsert image description here

summary

This article mainly introduces the characteristics of SpringSession, combined with the application in springboot, and multi-port access to session sharing. In the next article, we will explore the design structure of SpringSession.

Guess you like

Origin juejin.im/post/7233682328840306725