[springboot series] springboot integrates independent module redis to cache

Time:2019-12-2

Project GitHub address: https://github.com/5-ason/aso
Specific to see./db/db-redisand./db/db-cacheTwo modules

//Before todo integrates redis, you need to configure the redis environment locally. Later, you need to download and install redis under Linux

This paper mainly realizes the integration of independent modules for data operation. For details, please refer to another blog post:
[technical talks] independent modularization of data operation in spring cloud microservices

1. Objectives

The redis non relational database is independently deployed as the memory cache module to implement the cache operation relying on the cache module in the spring boot project, and the simple test is carried out.

2. Add dependency

<! -- spring boot redis depends on -- >
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<! -- spring boot cache dependency -- >
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

3. Configuration properties

Because my project uses spring cloud distributed configuration
So the configuration file is in. / config server / config repo / data-dev.yml. The specific configuration is as follows:

# ====================redis====================
redis:
  #Redis server address
  host: ason-hostname
  #Redis server connection port
  port: 6379
  #Redis server connection password (empty by default)
  password:
  #Connection timeout (MS)
  timeout: 0
  #Redis database index (0 by default)
  database: 0
  pool:
    #Maximum number of connections in the connection pool (use a negative value to indicate no limit)
    max-active: 8
    #Connection pool maximum block wait time (use a negative value to indicate no limit)
    max-wait: -1
    #Maximum free connections in connection pool
    max-idle: 8
    #Minimum free connections in connection pool
    min-idle: 0

4. Redisconf configuration

package com.ason;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Created by Ason on 2017-09-23.
 */
@Configuration
public class RedisConf {
    private static final Log log = LogFactory.getLog(RedisConf.class);

    @Value("${redis.host}")
    Private string host; // redis server address
    @Value("${redis.port}")
    Private int port; // redis server connection port
    @Value("${redis.password}")
    Private string password; // redis server connection password (default is empty)
    @Value("${redis.timeout}")
    Private int timeout; // connection timeout (MS)
    @Value("${redis.database}")
    Private int database; // connection timeout (MS)
    @Value("${redis.pool.max-active}")
    Private int maxtotal; // the maximum number of connections in the connection pool (a negative value indicates no limit)
    @Value("${redis.pool.max-wait}")
    Private int maxwaitmillis; // connection pool maximum blocking waiting time (use negative value to indicate no limit)
    @Value("${redis.pool.max-idle}")
    Private int maxidle; // the maximum free connection in the connection pool
    @Value("${redis.pool.min-idle}")
    Private int idle; // the minimum free connection in the connection pool



    /**
     *Configure jedispoolconfig
     *@ return jedispoolconfig entity
     */
    @Bean(name = "jedisPoolConfig")
    public JedisPoolConfig jedisPoolConfig() {
        Log.info ("initialize jedispoolconfig");
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        Jedispoolconfig. Setmaxtotal (this. Maxtotal); // the maximum number of connections in the connection pool (a negative value indicates no limit)
        Jedispoolconfig. Setmaxwaitmillis (this. Maxwaitmillis); // connection pool maximum blocking waiting time (use a negative value to indicate no limit)
        Jedispoolconfig. Setmaxidle (this. Maxidle); // the maximum free connection in the connection pool
        Jedispoolconfig. Setminidle (this. Minidle); // the minimum free connection in the connection pool
//        jedisPoolConfig.setTestOnBorrow(true);
//        jedisPoolConfig.setTestOnCreate(true);
//        jedisPoolConfig.setTestWhileIdle(true);
        return jedisPoolConfig;
    }

    /**
     *Instantiate redisconnectionfactory object
     * @param poolConfig
     * @return
     */
    @Bean(name = "jedisConnectionFactory")
    public RedisConnectionFactory jedisConnectionFactory(@Qualifier(value = "jedisPoolConfig") JedisPoolConfig poolConfig) {
        Log.info ("initialize redisconnectionfactory");
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);
        jedisConnectionFactory.setHostName(this.host);
        jedisConnectionFactory.setPort(this.port);
        jedisConnectionFactory.setDatabase(this.database);
        return jedisConnectionFactory;
    }

    /**
     *Instantiate redistemplate object
     * @return
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, String> functionDomainRedisTemplate(@Qualifier(value = "jedisConnectionFactory") RedisConnectionFactory factory) {
        Log.info ("initialize redistemplate");
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new EntityRedisSerializer());
        redisTemplate.setValueSerializer(new EntityRedisSerializer());
        redisTemplate.afterPropertiesSet();
        redisTemplate.setEnableTransactionSupport(true);
        return redisTemplate;
    }
}

Serialization related classes involved in redisconf

EntityRedisSerializer

package com.ason;

import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

/**
 *Custom redis serialization
 * Created by Ason on 2017-09-23.
 */
public class EntityRedisSerializer implements RedisSerializer<Object> {
    @Override
    public byte[] serialize(Object t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        return SerializeUtil.serialize(t);
    }

    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return SerializeUtil.unserialize(bytes);
    }
}

SerializeUtil

package com.ason;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 *Define serialization
 * Created by Ason on 2017-09-23.
 */
public class SerializeUtil {

    public static byte[] serialize(Object object) {
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try {
            // serialization
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] bytes = baos.toByteArray();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object unserialize(byte[] bytes) {
        ByteArrayInputStream bais = null;
        try {
            //Deserialization
            bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

5. Rediscacheconf configuration

package com.ason;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;

import java.lang.reflect.Method;

/**
 * Created by Ason on 2017/9/25.
 *The main purpose of implementing cachengconfigurersupport here is to facilitate the use of a custom keygenerator
 */
@Configuration
@Enablecaching // enable caching
public class RedisCacheConf  extends CachingConfigurerSupport {

    @Autowired
    private RedisTemplate redisTemplate;

    private static final Log log = LogFactory.getLog(RedisConf.class);
    /**
     *Configure redis cache management object
     * @return
     */
    @Bean(name = "cacheManager")
    public CacheManager cacheManager() {
        Log.info ("initialize CacheManager");
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
//        Map<String, Long> expires = new HashMap<>();
//        expires.put("cache:user", 36000L);
//        cacheManager.setExpires(expires);
        //Set cache expiration time
        //cacheManager.setDefaultExpiration(10000);
        return cacheManager;
    }

    /**
     *The strategy of generating key
     *This method will generate a unique key based on the value of class name + method name + all parameters. Even if the value attribute in @ cacheable is the same, the key will be different.
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}

6. Specific use

Because the configuration of redis is used as a module DB redis, the configuration module of cache manager DB cache depends on DB redis:

<dependency>
    <groupId>com.ason</groupId>
    <artifactId>db-redis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

Therefore, if my RMS service microservice wants to cache, it needs to rely on the DB cache module:

<dependency>
    <groupId>com.ason</groupId>
    <artifactId>db-cache</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

At the same time, you need to read the redis configuration attributes. My configuration above is under. / config server / config repo / data dev.yml. Therefore, in the bootstrap.yml configuration file under the RMS service microservice, you need to specify the address of the configuration center service and the name and profile of the configuration file:

spring:
  #Address of configuration center service
  cloud:
    config:
      name: data
      Profile: ${spring. Profiles. Active} - profile property of the profile to read
#      uri: http://127.0.0.1:7001
      #label: ${spring.profiles.active}
      discovery:
        Enabled: true ා default false. Set to true to use the configserver configuration in the registry instead of configuring the URI of the configserver itself
        serviceId: config-server
  profiles:
    active: dev

Next, you can use the cache related annotations to use the cache under the RMS service microservice, and add the following in the rmsusercontroller (only for testing, and adjust according to the actual development requirements):

/**
 *Query individual users
 */
@Cacheable(value = "usercache", key = "#account")
@PostMapping(value = "/account", produces = "application/json;charset=UTF-8")
public String findUserByAccout(@RequestParam("account") String account) throws Exception {
    return ResultBody.success(rmsUserService.selectUserByAccout(account));
}

Start project, post request to visit http: / / localhost: 8888 / RMS / use

[springboot series] springboot integrates independent module redis to cache

Next, go to redis and find that [email protected] has been saved as a key:

[springboot series] springboot integrates independent module redis to cache

7. Precautions

In some cases, the betting solution cache will not work (the pit I encountered in my first integration test):
The same bean internal method calls, subclass calls methods with cache annotations in the parent class, etc.


So far, the spring boot integration independent module redis has been completed for caching
For details, please see GitHub address: https://github.com/5-ason/aso

Related articles:
[springboot series] springboot integrates independent modules Druid + mybatis plus