Cheap and inexpensive approach - springboot cluster scenarios on small and micro enterprise clouds 2:session and redis

Posted by Shuriken1 on Sun, 08 Sep 2019 19:50:03 +0200

When talking about the cluster scenario, the first problem you will encounter is session. On a single machine, session problems are always solved by web containers, which we mainly use, but clustering means multiple containers.If load balancing is to randomly distribute server access, it is easy to cause server A to log on and then go to server B next time. As a result, there is no session for that user in the web container of server B, and the result is tragic.So what to do? Of course, redis handles it. redis stores sessions centrally. No matter which server accesses the sessions, redis does not save sessions. This problem is perfectly solved.The first thing I think about is Spring's own solution, spring session.

1. spring session+redis Run

The scheme for spring session+redis is very simple. Please follow the steps:

Step 1: Add starter to pom file

        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>

spring-boot-starter-web is the introduction of web dependency, spring-boot-starter-data-redis is the access to redis, and spring-session-data-redis is the dependency on redis as the storage location of sessions and related operations.

Step 2: Write the redis configuration into application.properties

# REDIS
# Redis database index (default 0)
spring.redis.database=0  
# Redis Server Address
spring.redis.host=myip
# Redis Server Connection Port
spring.redis.port=6379  
# Redis server connection password (blank by default)
spring.redis.password=password
# Maximum number of connections in connection pool (no limit with negative value) default 8
spring.redis.lettuce.pool.max-active=8
# Connection pool maximum blocking wait time (unlimited with negative value) default-1
spring.redis.lettuce.pool.max-wait=-1
# Maximum idle connection in connection pool default 8
spring.redis.lettuce.pool.max-idle=8
# Minimum idle connection in connection pool default 0
spring.redis.lettuce.pool.min-idle=0

Step 3: Add comments to the startup class

@SpringBootApplication
@EnableCaching
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class RedisApplication {

	public static void main(String[] args) {
		SpringApplication.run(RedisApplication.class, args);
	}
}

Where @EnableCaching indicates opening the cache because we have redis configured in the application.proerties file, so the default redis is the cache for the project; @EnableRedisHttpSession indicates that redis stores httpsession, and subsequent sessions are automatically stored in redis.

Let's set up a controller and try if this is the case:

@RestController
public class IndexController {
  
    @RequestMapping("/saveSession")
    String saveSession(HttpSession session) {
        session.setAttribute("mySession", "lalala");
        return session.getId();
    }
    
    @RequestMapping("/getSession")
    String getSession(HttpSession session) {
        String mySessionString = session.getAttribute("mySession").toString();
        return mySessionString;
    }

The way to save Session and getSession is simple: one to save session, one to take session

Let's use redis-cli to see if this session has been saved to redis:

Visible, sessions are automatically stored in redis.Visible, it's very simple to implement sessions to redis and then share them.

The source code for this block can be Look here

2. session's storage structure in redis

When you look at sessions with redis-cli above, you can see that sessions are actually stored, but the way they are stored is not so clear, so you can talk about it.

2.1, namespace, and other properties

The first is the stored key value, such as in the screenshot above, which is a section like this:

spring:session:sessions:54abb3f7-909a-46c8-ab4c-1b515eff69b1

Where spring:session is the namespace of the spring session within redis, the default is "spring:session", which can be seen in the source code of org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration:

This namespace is modifiable, and as we can see from the source code of @EnableRedisHttpSession, there are several parameters that can be passed in to configure

public @interface EnableRedisHttpSession {

	/**
	 * The session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
	 * This should be a non-negative integer.
	 * @return the seconds a session can be inactive before expiring
	 */
	int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;

	/**
	 * Defines a unique namespace for keys. The value is used to isolate sessions by
	 * changing the prefix from default {@code spring:session:} to
	 * {@code <redisNamespace>:}.
	 * <p>
	 * For example, if you had an application named "Application A" that needed to keep
	 * the sessions isolated from "Application B" you could set two different values for
	 * the applications and they could function within the same Redis instance.
	 * @return the unique namespace for keys
	 */
	String redisNamespace() default RedisOperationsSessionRepository.DEFAULT_NAMESPACE;

	/**
	 * Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
	 * updates the backing Redis when {@link SessionRepository#save(Session)} is invoked.
	 * In a web environment this happens just before the HTTP response is committed.
	 * <p>
	 * Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
	 * Session are immediately written to the Redis instance.
	 * @return the {@link RedisFlushMode} to use
	 * @since 1.1
	 */
	RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;

	/**
	 * The cron expression for expired session cleanup job. By default runs every minute.
	 * @return the session cleanup cron expression
	 * @since 2.0.0
	 */
	String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;

}
  • maxInactiveIntervalInSeconds is the expiration time of data in sessions (not sessions in redis)
  • redisNamespace is the namespace of session's key inside redis in spring session
  • redisFlushMode is how redis saves sessions, defaulting on_SAVE.There are two ways to do this:? IMMEDIATE: Save session as soon as it is created; ON_SAVE: Save session when it is created, but save when data is added to the current session
  • Data sweep timer task when cleanupCron session expires, default configuration time is 1 minute, why configure this, will be discussed later.

Let's see how this works by configuring a namespace at @EnableRedisHttpSession

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30,redisNamespace = "wphmoon:session")
public class RedisApplication {
......
}

Visit the saveSession method above to see the data structure of the session in redis:

2.2 session Storage Structure in redis

As you can see from the above diagram, the namespace has been modified.Let's see what the value after key looks like again

Failed because session s are stored in redis in a format other than string type

With hash, let's look at it with hget

Not found, because mySession is not a complete field name, it is

When you see the familiar lalalala, you know that this time it's finally found that the complete fieldName will be preceded by the sessionAttr we named Attribute.But what the hell is'xacxedx00x05tx00x06'in front of lalalala?

That's all about flipping the source code, so I started looking in spring-session-data-redis-XXX.jar and saw the SpringSessionRedisOperations class, which looks like the operation class that pushes sessions to redis (it's a comment itself, and the author in the code is very kind to tell us where to go about the implementationSome).

In the RedisOperationsSessionRepository class, I unexpectedly found this

The original sessionAttr: was defined here, and I found this

The original namespace was here to add sessions, but the main reason we flipped the code this time was to see that the random strings in the value content were not there, in another type of ReactiveRedisOperations SessionRepository.It actually operates on session storage by calling another interface class:

Ultimately, there are only two implementation classes for this interface, or inheritance relationships.

Seeing RedisTemplate finally saw old friends, the tools we use most often when using redis.See how it operates on hash

As you can see from the name of the parameter, this must have been serialized, so you can see it in the RedisSerializationContext

It seems that all session s need to be serialized when they are stored in redis, and the heap before the real string is the serialized tag content.

That's all in this chapter, and in the next chapter, we'll continue to soak in spring session s and redis and talk about cleanupCron (remember where it happened).

Topics: Programming Redis Session Spring Database