Distributed Session solution

  • Problems with distributed sessions?
  • Problems existing in the current project
  • How to solve the sharing problem between these two services?

Distributed Session consistency?

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

The role of Session?

Session is the communication session tracking technology between the client and the server. The server and the client maintain the basic information of the whole communication session. Identify user login identity, security verification, etc.

When the client accesses the server for the first time, the server will respond to a sessionid and store it in the local cookie. In subsequent accesses, 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 that the first time service A is accessed, A sessionid is generated and stored in the cookie, and the second time service B is accessed, the client will read the sessionid in the cookie and add it to the request header. If service B does not find the corresponding data through the sessionid, it will create A new one and return the sessionid to the client. In this way, our Session cannot be shared, and our desired purpose cannot be achieved.

Solution:

  • Use cookie s to do this (obviously this unsafe operation is not reliable)
  • Using the ip binding policy in Nginx, the same ip can only be accessed on the same specified machine (load balancing is not supported)
  • Using database to synchronize session s (inefficient)
  • Use tomcat's built-in session synchronization (synchronization may cause delay)
  • Use token instead of session
  • Use shared redis to store session s

For spring projects, we use spring sessions and integrated solutions, which are stored in redis

Problems existing in the current project

The port numbers of the two startup projects are 8081 respectively.

Dependency:

<!--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 rely on-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
</dependencies>

To create a test class:

@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() + "stay Session Successfully saved in!";
    }

    @RequestMapping("/getSession")
    public String getSession(HttpSession session) {
        return "Current project port:" + projectPort + " current sessionId :" + session.getId() + "  Get your name:" + session.getAttribute("name");
    }

}

yml configuration:

server:
  port: 8080

Modify mapping file

#Map native ip to www.hello.com COM
127.0.0.1 www.hello.com

Here we start nginx cluster and modify the configuration:

#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 directly through the polling mechanism. First, we deposit a name into the Session, http://www.hello.com/createSession?name=SimpleWu

Current project port: 8081 current sessionid: 0f20f73170ae6780b1ec06d9b06210db successfully saved in Session!

Because we use the default polling mechanism, we must visit port 8080 next time. We directly obtain the values just stored below http://www.hello.com/getSession

Current project port: 8080 current sessionid: c6663ea93572fb8dae27736a553eab89 obtained Name: null

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

Don't hurry to continue to visit, because the polling mechanism is that at this time, we are a server with port 8081, so we previously saved a name in port 8081. 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

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

Why haven't we saved the 8080 port, not even the 8081 port?

Let's take a closer look. The sessionid of the port we visited 8081 for the third time is different because we visited port 8080 for the second time. At this time, the client obtained the port of 8081 in the cookie and went to the 8080 server to find it. If it was not found, it recreated a session and responded to the client with the sessionid. The client maintained the cookie and replaced the sessionid of the previous 8081, Then, during the third visit, take the sessionid of the second visit to find it, but it can't be found, and then create it again. Keep cycling.

How to solve the sharing problem between these two services?

Spring has given us a good idea of the problem and has provided a solution: those who do not know about spring session can go to Baidu to learn about it.

We first open redis and open it in POM Add dependency to XML:

		<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>
        <!--spring session And redis Application basic environment configuration,Need to open redis It can only be used after, otherwise it can be started Spring boot Will report an error -->
        <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:

server:
  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

# Custom configuration
redis:
 hostname: localhost
 port: 6379
 #password: 123456

Add Session configuration class

/**
 * session to configure
 * 
 */
//This class is used to configure the connection of redis server
//maxInactiveIntervalInSeconds is the expiration time of spring session (unit: seconds)
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class SessionConfig {

    // The value after the colon is the default value of braking loading when there is no configuration file
    @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

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

Then we continue to start 8081 to test:

First deposit a name http://www.hello.com/createSession?name=SimpleWu :

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

It should be the polling mechanism. Next time, it must be 8081. The problem of session sharing has been solved. Then we can get the name directly http://www.hello.com/getSession :

Current project port: 8081 current sessionid: cf5c029a-2f90-4b7e-8345-bf61e0279254 obtained Name: SimpleWu

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

Implementation principle:

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

reference

Author: SimpleWu

source: https://www.cnblogs.com/SimpleWu/p/10118674.html

Please indicate the author and the publishing location of the article in "simple. 4" on this site.

Tags: Java Distribution Nginx Redis

Posted by m@tt on Fri, 15 Apr 2022 04:01:18 +0930