Java distributed session sharing solution

Time:2020-7-7

Distributed session solution

CopyAuthor:SimpleWu

Distributed session consistency?

To put it bluntly, it is the problem of session sharing in the server cluster

The role of session?

Session is a session tracking technology for communication between client and server. The server and client keep the basic information of the whole communication session.

When the client visits the server for the first time, the server will respond to a sessionid and store it in the local cookie. In the subsequent access, the sessionid in the cookie will be put into the request header to access the server. If the corresponding data is not found through this sessionid, the server will create a new sessionid and respond to the client.

Problems with distributed sessions?

Suppose the first visit to service a generates a sessionid and stores it in the cookie, but the second time it accesses service B, the client will read the sessionid in the cookie and add it to the request header. If service B fails to find the corresponding data through the sessionid, it creates a new one and returns the sessionid to the client, In this way, we can’t share our sessions, and we can’t achieve the goal we want.

Solution:

  • Use cookies to do this (obviously this unsafe operation is unreliable)
  • Using the IP binding policy in nginx, the same IP can only be accessed in the same specified machine (load balancing is not supported)
  • Using database to synchronize session (inefficient)
  • Use Tomcat’s built-in session synchronization (synchronization may cause delays)
  • Use token instead of session
  • We use spring session and integrated solutions, which are stored in redis
Problems in the current project

The port numbers of the two projects are 80808081.

Dependence:

Copy <! -- springboot parent project -- >
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
          <! -- Web dependency -- >
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
</dependencies>

To create a test class:

Copy/**
 * Author: SimpleWu
 * date: 2018/12/14
 */
@RestController
public class TestSessionController {

    @Value("${server.port}")
    Private integer projectport; // Project port

    @RequestMapping("/createSession")
    public String createSession(HttpSession session, String name) {
        session.setAttribute("name", name);
        Return "current project port: + projectport +" current sessionid ":+ session.getId () + "successfully saved in session! ";
    }

    @RequestMapping("/getSession")
    public String getSession(HttpSession session) {
        Return "current project port: + projectport +" current sessionid ":+ session.getId () + "name obtained:+ session.getAttribute ("name");
    }

}

YML configuration:

Copyserver:
  port: 8080 

Modify mapping file

Copy ා maps native IP to www.hello.com upper
127.0.0.1 www.hello.com

Here, we start the nginx cluster and modify the configuration:

Copy ා join
#Polling is used by default,
upstream backserver{
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
}
#Modify local in server
location / {
            proxy_pass  http://backserver;
            index  index.html index.htm;
        }

We access the session directly through the polling mechanism. First, we put a name into the session, http://www.hello.com/createSession?name=SimpleWu

Copy current project port: 8081 current sessionid: 0f20f73170ae6780b1ec06d9b06210db saved successfully in session!

Because we use the default polling mechanism, we will definitely visit port 8080 next time. We will directly obtain the following values that have been stored http://www.hello.com/getSession

Copy current project port: 8080 current sessionid: c6663ea93572fb8dae27736a553eab89 name obtained: null

At this time, we found that there is no value stored in port 8080, and the sessionid is different from that in port 8081.

Don’t worry to continue to visit, because we are the server of port 8081 at this time of polling mechanism, so we have saved a name in 8081 before. Now let’s visit the following to see if we can get the name we deposited: simplewu, continue to visit: http://www.hello.com/getSession

Copy current project port: 8081 current sessionid: 005ee6198c30d7cd32fbd8b0735331347 obtained Name: null

We didn’t save port 8080, even port 8081?

We carefully observe that the session ID of the third visit to the 8081 port is different. The reason is that when we visit port 8080 the second time, the client gets the port 8081 in the cookie and goes to the 8080 server to find it. If it is not found, it recreates a session and responds the sessionid to the client, and the client keeps it in the cookie and replaces it For the session ID of 8081 before, you can find the session ID of the second visit and then create it. It’s been repeated.

How to solve the problem of sharing between the two services?

Spring has given us a good idea of the problem and has provided a solution: the spring session can go to Baidu to understand.

Let’s open redis first, and then open the pom.xml Add dependency in:

Copy<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <! -- the basic environment configuration of spring session and redis should be enabled before it can be used. Otherwise, an error will be reported when starting spring boot -- >
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

Modify YML configuration file:

Copyserver:
  port: 8081
spring:
  redis:
    database: 0
    host: localhost
    port: 6379
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
    timeout: 10000
redis:
 hostname: localhost
 port: 6379
 #password: 123456

Add session configuration class

Copy/**
 * Author: SimpleWu
 * date: 2018/12/14
 */
//This class is used to configure the connection of redis server
//Maxinactive intervalinseconds is the expiration time (in seconds) of the spring session
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class SessionConfig {

    //The value after the colon is the default value for brake loading when there is no profile
    @Value("${redis.hostname:localhost}")
    private String hostName;
    @Value("${redis.port:6379}")
    private int port;
   // @Value("${redis.password}")
   // private String password;

    @Bean
    public JedisConnectionFactory connectionFactory() {
        JedisConnectionFactory connection = new JedisConnectionFactory();
        connection.setPort(port);
        connection.setHostName(hostName);
        //connection.setPassword(password);
        // connection.setDatabase(0);
        return connection;
    }
}

Initialize session configuration

Copy/**
 * Author: SimpleWu
 * date: 2018/12/14
 */
//Initialize session configuration
public class SessionInitializer extends AbstractHttpSessionApplicationInitializer {
    public SessionInitializer() {
        super(SessionConfig.class);
    }
}

Then we continue to start 80808081 to test:

Save a name first http://www.hello.com/createSession?name=SimpleWu :

Copy current project port: 8080 current sessionid: cf5c029a-2f90-4b7e-8345-bf61e0279254 saved in session successfully!

If the polling mechanism should be used, then next time it must be 8081, and the session sharing problem has been solved. Then we must be able to obtain it. In this way, we can get the name directly http://www.hello.com/getSession :

Copy current project port: 8081 current sessionid: cf5c029a-2f90-4b7e-8345-bf61e0279254 name obtained: simplewu

At this time, we found that not only can we get the value, but also the sessionid is consistent.

Implementation principle:

After the web server receives the HTTP request, when the request enters the corresponding filter for filtering, the process of creating a session by the web server is transferred to spring session for creation. The originally created session is saved in the memory of the web server, and the session information created through spring session can be saved in the third-party services, such as redis, mysql, etc. Web servers share data by connecting with third-party services to realize session sharing!

Previous articles

Nginx series of tutorials (1) basic introduction and installation of nginx
Nginx series of tutorials (2) nginx building static resource web server
Nginx series of tutorials (3) nginx cache server static files
Nginx series of tutorials (4) nginx deals with web application load balancing to ensure high concurrency
How to ensure the high availability of nginx (5)
Nginx series of tutorials (6) detailed explanation of nginx location matching rules
Nginx series tutorial (7) detailed description of nginx rewrite configuration rules
Nginx series of tutorials (8) nginx configuration security certificate SSL
Nginx series of tutorials (9) nginx Solving Session consistency