Spring Boot: Redis cache usage

1. Introduction to redis
Redis is the most widely used memory data storage in the industry. Compared with Memcached, redis supports richer data structures, such as hashes, lists and sets, and supports data persistence. In addition, redis also provides some database like features, such as transaction, HA and master-slave database. It can be said that redis has some characteristics of both caching system and database, so it has rich application scenarios. This article introduces two typical application scenarios of redis in Spring Boot.

2. Introduction to lettuce
If Redis cache has been used in Java applications, you must be familiar with jedis. Like jedis, lattice is a client program connecting to Redis Server. Jedis is directly connected to Redis Server in terms of implementation. It is not thread safe in multi-threaded environment, unless connection pool is used to add physical connection for each jedis instance. Lettue's Netty based connection instance (stateful redisconnection) can be accessed concurrently among multiple threads and is thread safe. It meets the concurrent access in a multithreaded environment. At the same time, it is a scalable design. If a connection instance is not enough, you can also add connection instances as needed.
Understand the source code + request: 1791743380

3. Usage in spring boot application
Directly use RedisTemplate
Integrating Redis with Spring Cache
Session sharing through Spring Session
4. Engineering practice
4.1 project dependent POM The XML is as follows:
Code listing: spring boot redis / POM xml

<dependencies>
    <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>
    <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>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Spring boot starter data redis: in spring boot 2 After X, the bottom layer is no longer Jedis, but Lettuce, as shown in the figure:

commons-pool2: used as a redis connection pool. An error will be reported if startup is not introduced.

Spring Session data redis: spring Session is introduced and used as a shared Session.

4.2 configuration file application yml
Code listing: spring boot redis / SRC / main / resources / application yml

server:
  port: 8080
  servlet:
    session:
      timeout: 30m
spring:
  application:
    name: spring-boot-redis
  cache:
    # After using Spring Cache, you can specify Spring Cache. Type is manually specified. Although it will automatically adapt the dependency of the existing Cache, the order will affect the use of Redis (jcache - > ehcache - > Redis - > guava)
    type: REDIS
  redis:
    host: 192.168.0.128
    port: 6379
    password: 123456
    # Connection timeout (ms)
    timeout: 10000
    # Redis has 16 partitions by default. The specific partitions configured here are 0 by default
    database: 0
    lettuce:
      pool:
        # The maximum number of connections in the connection pool (negative value indicates no limit) is 8 by default
        max-active: 100
        # Maximum blocking waiting time of connection pool (negative value indicates no limit) default - 1
        max-wait: -1
        # The maximum free connections in the connection pool are 8 by default
        max-idle: 8
        # The minimum free connections in the connection pool are 0 by default
        min-idle: 0

There is not much explanation for the configuration here. Notes have been marked to explain.

4.3 RedisTemplate usage
4.3. 1. Create entity class user java
Code listing: spring boot redis / SRC / main / Java / COM / springboot / springbootredis / model / user java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 662692455422902539L;
    private Long id;
    private String name;
    private int age;
}
**4.3.2 custom RedisTemplate**
By default, templates can only support RedisTemplate<String, String> ,That is, you can only store strings, which is unfriendly in development, so it is necessary to customize the template. When you customize the template, you want to use it again String Storage can be used at this time StringRedisTemplate In a way that they do not conflict, add configuration classes RedisCacheConfig.java ,The code is as follows:

Code list: spring-boot-redis/src/main/java/com/springboot/springbootredis/config/RedisCacheConfig.java

```java
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisCacheConfig {

    @Bean
    public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

4.3. 3. Test interface usercontroller java
Code list:

@RestController
@Slf4j
public class UserController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    RedisTemplate<String, Serializable> redisCacheTemplate;

    @Autowired
    UserService userService;

    @GetMapping("/test")
    public void test() {
        stringRedisTemplate.opsForValue().set("geekdigging", "https://www.geekdigging.com/");

        log.info("Current get object:{}",stringRedisTemplate.opsForValue().get("geekdigging"));

        redisCacheTemplate.opsForValue().set("geekdigging.com", new User(1L, "geekdigging", 18));

        User user = (User) redisCacheTemplate.opsForValue().get("geekdigging.com");

        log.info("Current get object:{}", user);
    }
}

4.3. 4 test
Start the service and open the browser access link: http://localhost:8080/test , view the console log print as follows:

2019-09-24 23:49:30.191  INFO 19108 --- [nio-8080-exec-1] c.s.s.controller.UserController          : Current get object: https://www.geekdigging.com/
2019-09-24 23:49:30.243  INFO 19108 --- [nio-8080-exec-1] c.s.s.controller.UserController          : Current get object: User(id=1, name=geekdigging, age=18)

The test was successful.

4.4 integrating Redis with Spring Cache
4.4.1 Spring Cache features
Spring 3.1 introduces the exciting annotation based cache technology. In essence, it is not a specific cache implementation scheme (such as EHCache or Redis), but an abstraction of cache use. By adding a small amount of various annotations it defines to the existing code, it can achieve the effect of caching the return object of the method.

Spring Cache has quite good flexibility. It can not only use Spring Expression Language (SpEL) to define cache key s and various condition s, but also provide out of the box cache temporary storage scheme. It also supports the integration with mainstream professional caches such as EHCache, Redis and Guava.

Based on annotation, existing code can support caching
Out of the box, cache can be used without installing and deploying additional third-party components
It supports Spring Express Language and can use any attribute or method of the object to define the cached key and condition
Support AspectJ and implement caching support for any method through it
It supports user-defined key s and user-defined cache managers, and has considerable flexibility and scalability
4.4. 2. Define the interface userservice java
Code listing: spring boot redis / SRC / main / Java / COM / springboot / springbootredis / service / userservice java

public interface UserService {
    User save(User user);

    User get(Long id);

    void delete(Long id);
}

4.4. 3 interface implements userserviceimpl java
Code listing: spring boot redis / SRC / main / Java / COM / springboot / springbootredis / service / impl / userserviceimpl java

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    private static final Map<Long, User> USER_MAP = new HashMap<>();

    static {
        USER_MAP.put(1L, new User(1L, "geekdigging.com", 18));
        USER_MAP.put(2L, new User(2L, "geekdigging.com", 19));
        USER_MAP.put(3L, new User(3L, "geekdigging.com", 20));
    }

    @CachePut(value = "user", key = "#user.id")
    @Override
    public User save(User user) {
        USER_MAP.put(user.getId(), user);
        log.info("get into save Method, current storage object:{}",  user);
        return user;
    }

    @Cacheable(value = "user", key = "#id")
    @Override
    public User get(Long id) {
        log.info("get into get Method to get the current object:{}",  USER_MAP.get(id));
        return USER_MAP.get(id);
    }

    @CacheEvict(value = "user", key = "#id")
    @Override
    public void delete(Long id) {
        USER_MAP.remove(id);
        log.info("get into delete Method, deleted successfully");
    }
}

To facilitate the demonstration of database operation, a map < long, user > user is defined directly_ The core of map is the three annotations @ Cacheable, @ CachePut and @ CacheEvict.

4.4.3.1 @Cacheable
Cache the results according to the request parameters of the method

Key: the cached key, which can be empty. If it is specified to be written according to the spiel expression, if it is not specified, it will be combined according to all the parameters of the method by default (e.g. @ Cacheable(value = "user", key="#userName"))

Value: the name of the cache. At least one must be specified (e.g. @ Cacheable(value = "user") or @ Cacheable(value = {"user1", "use2"}))

Condition: the condition of the cache, which can be empty. It is written in spiel and returns true or false. Only when it is true can the cache be performed (e.g. @ Cacheable(value = "user", key = "#id", condition = "#id < 10"))

4.4.3.2 @CachePut
The result is cached according to the request parameters of the method. Unlike @ Cacheable, it will trigger the call of the real method every time

key: same as above
value: same as above
condition: same as above
4.4.3.3 @CachEvict
Empty the cache according to the condition

key: same as above
value: same as above
condition: same as above
allEntries: whether to clear all cache contents. The default is false. If it is specified as true, all caches will be cleared immediately after the method call (e.g. @ CacheEvict(value = "user", key = "#id", allEntries = true))
beforeInvocation: whether to clear the method before execution. The default is false. If it is specified as true, the cache will be cleared before the method is executed. By default, if the method throws an exception, the cache will not be cleared (e.g. @ CacheEvict(value = "user", key = "#id", beforeInvocation = true))
4.4. 4 start main class
Code listing: spring boot redis / SRC / main / Java / COM / springboot / springbootredis / springbootredisapplication java

@SpringBootApplication
@EnableCaching
public class SpringBootRedisApplication {

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

}

The annotation @ EnableCaching needs to be added here to enable Spring Session.
4.4. 5 add test interface
Code listing: spring boot redis / SRC / main / Java / COM / springboot / springbootedis / controller / usercontroller java

@GetMapping("/test1")
public void test1() {
    User user = userService.save(new User(4L, "geekdigging.com", 35));

    log.info("current save Object:{}", user);

    user = userService.get(1L);

    log.info("current get Object:{}", user);

    userService.delete(5L);
}

4.4. 6 test
Start the service and open the browser access link: http://localhost:8080/test , refresh the page and print the console log as follows:

2019-09-25 00:07:21.887  INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl       : get into save Method, current storage object: User(id=4, name=geekdigging.com, age=35)
2019-09-25 00:07:21.897  INFO 21484 --- [nio-8080-exec-1] c.s.s.controller.UserController          : current save Object: User(id=4, name=geekdigging.com, age=35)
2019-09-25 00:07:21.899  INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl       : get into get Method to get the current object: User(id=1, name=geekdigging.com, age=18)
2019-09-25 00:07:21.900  INFO 21484 --- [nio-8080-exec-1] c.s.s.controller.UserController          : current get Object: User(id=1, name=geekdigging.com, age=18)
2019-09-25 00:07:21.901  INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl       : get into delete Method, deleted successfully

Refresh the page again to view the console log:

2019-09-25 00:08:54.076  INFO 21484 --- [nio-8080-exec-7] c.s.s.service.impl.UserServiceImpl       : get into save Method, current storage object: User(id=4, name=geekdigging.com, age=35)
2019-09-25 00:08:54.077  INFO 21484 --- [nio-8080-exec-7] c.s.s.controller.UserController          : current save Object: User(id=4, name=geekdigging.com, age=35)
2019-09-25 00:08:54.079  INFO 21484 --- [nio-8080-exec-7] c.s.s.controller.UserController          : current get Object: User(id=1, name=geekdigging.com, age=18)
2019-09-25 00:08:54.079  INFO 21484 --- [nio-8080-exec-7] c.s.s.service.impl.UserServiceImpl       : get into delete Method, deleted successfully

The results are consistent with our expectations. We can see that the query has no log output in the addition, deletion and modification query, because it directly obtains the data from the cache, and the addition, modification and deletion will enter the UserServiceImpl method to execute the specific business code.

4.5 Session sharing
4.5. 1 Introduction to spring session
Spring Session provides a scheme for creating and managing servlet httpsessions. Spring Session provides the function of Clustered Sessions. By default, external Redis is used to store Session data to solve the problem of Session sharing.

4.5. 2. Start the main class springbootredisapplication java
Code listing: spring boot redis / SRC / main / Java / COM / springboot / springbootredis / springbootredisapplication java

@SpringBootApplication
@EnableCaching
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class SpringBootRedisApplication {

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

}

Maxinactivitintervalinseconds: set the Session expiration time. After using Spring Session, the original Spring Boot configuration file application Server in YML Session. The timeout property is no longer valid.
4.5. 3 add test interface
Code listing: spring boot redis / SRC / main / Java / COM / springboot / springbootedis / controller / usercontroller java

@GetMapping("/getBlogUrl")
public String getSessionId(HttpServletRequest request) {
    String url = (String) request.getSession().getAttribute("url");
    if (StringUtils.isEmpty(url)) {
        request.getSession().setAttribute("url", "https://www.geekdigging.com/");
    }
    log.info("obtain session The content is: {}", request.getSession().getAttribute("url"));
    return request.getRequestedSessionId();
}

4.5. 4 test
Start the service and open the browser access link: http://localhost:8080/getBlogUrl , view the current storage content of Redis, as shown in the following figure:

Where 156933918000 is the expiration time, which means that the Session fails after this time, and b2522824-1094-478e-a435-554a551bc8bb is the SessionId.

4.5. 6 how to share sessions in multiple services
Follow the above steps to configure it again in another project. After startup, Session sharing will be carried out automatically.

Keywords: Java Redis Cache

Added by py343 on Thu, 30 Dec 2021 23:57:46 +0200