All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.luues.redis.single.config.RedisConfig Maven / Gradle / Ivy

package com.luues.redis.single.config;

import cn.luues.tool.crypto.CryptoUtils;
import cn.luues.tool.json.JsonUtils;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.luues.exception.core.exception.RedisException;
import com.luues.redis.config.CustomizedRedisCacheManager;
import com.luues.redis.config.JedisConfig;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import redis.clients.jedis.*;

import java.lang.reflect.Field;
import java.time.Duration;
import java.util.*;

/**
 * redis配置类
 *
 * @author Mr-Wu ON 2019/05/07
 **/
@ConditionalOnExpression("!'${spring.redis.host:null}'.equals('null')")
@Component(value = "redis_config")
@ConfigurationProperties(prefix = "spring.redis")
@EnableCaching//开启注解
@Data
@Slf4j(topic = "c.l.r.s.c.r")
public class RedisConfig extends CachingConfigurerSupport {
    private Integer maxIdle = 300;
    private Integer maxTotal = 1000;
    private Integer maxWaitMillis = 1000;
    //JedisPool中连接的空闲时间阈值,当达到这个阈值时,空闲连接就会被移除。Redis的默认值是30分钟,太长,所以JedisPoolConfig的默认值是1分钟;
    private Integer minEvictableIdleTimeMillis = 60000;
    //每次检测时,取多少个连接进行检测。如果设置成-1,就表示检测所有链接
    private Integer numTestsPerEvictionRun = -1;
    //检测空闲连接的周期
    private Long timeBetweenEvictionRunsMillis = (long)30000;
    private Boolean testOnBorrow = true;
    private Boolean testOnReturn = true;
    private Boolean testWhileIdle = true;
    private String host;
    private Integer port = 6379;
    private Integer timeout = 2000;
    private String prefix;
    private String password;
    private Integer database = 0;

    public String getHost() {
        return CryptoUtils.initDecrypt(host);
    }
    public String getPrefix() {
        return CryptoUtils.initDecrypt(prefix);
    }
    public String getPassword() {
        return CryptoUtils.initDecrypt(password);
    }

    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大空闲数
        jedisPoolConfig.setMaxIdle(maxIdle);
        // 连接池的最大数据库连接数
        jedisPoolConfig.setMaxTotal(maxTotal);
        // 最大建立连接等待时间
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
        jedisPoolConfig.setMinEvictableIdleTimeMillis(maxWaitMillis);
        // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
        jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
        // 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        // 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
        jedisPoolConfig.setTestOnBorrow(testOnBorrow);
        // 在return一个jedis实例时,是否提前进行validate操作
        jedisPoolConfig.setTestOnReturn(testOnReturn);
        jedisPoolConfig.setTestOnCreate(true);
        // 在空闲时检查有效性, 默认false
        jedisPoolConfig.setTestWhileIdle(testWhileIdle);
        log.info("jedis - Starting...");
        return jedisPoolConfig;
    }

    @Bean
    @Qualifier(value = "jedisShardInfos")
    public List jedisShardInfos() throws NoSuchFieldException, IllegalAccessException {
        List shards = new ArrayList();
        JedisShardInfo jedisShardInfo = new JedisShardInfo(getHost(), getPort());
        if(Strings.isNotBlank(password)){
            jedisShardInfo.setPassword(getPassword());
        }
        Class clz = jedisShardInfo.getClass();
        Field declaredField = clz.getDeclaredField("db");
        declaredField.setAccessible(true);
        declaredField.set(jedisShardInfo, database);
        shards.add(jedisShardInfo);
        return shards;
    }

    @Bean
    public ShardedJedisPool shardedJedisPool(JedisPoolConfig jedisPoolConfig, @Qualifier(value = "jedisShardInfos") List jedisShardInfos) {
        ShardedJedisPool shardedJedisPool = new ShardedJedisPool(jedisPoolConfig, jedisShardInfos);
        return shardedJedisPool;
    }

    @Bean
    public JedisPool jedisPool(JedisPoolConfig jedisPoolConfig) throws RedisException {
        JedisPool jedisPool = null;
        if(Strings.isNotBlank(password)){
            jedisPool = new JedisPool(jedisPoolConfig, getHost(), port, timeout, getPassword(), database);
        } else {
            jedisPool = new JedisPool(jedisPoolConfig, getHost(), port, timeout, null, database);
        }
        try {
            jedisPool.getResource().get("-test-");
            log.info("jedis - Start completed.");
        }catch (Exception e){
            log.error("jedis - Start fail. error:{}", e.getMessage());
            log.info("config:{}", JsonUtils.toJson(this));
            throw new RedisException("redis start fail");
        }
        return jedisPool;
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(getHost());
        redisStandaloneConfiguration.setDatabase(database);
        redisStandaloneConfiguration.setPort(port);
        if(Strings.isNotBlank(password)){
            redisStandaloneConfiguration.setPassword(RedisPassword.of(getPassword()));
        }
        return new JedisConnectionFactory(redisStandaloneConfiguration);
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean({CacheManagerProperties.class})
    @DependsOn({"springContextHolder"})
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, CacheManagerProperties cacheManagerProperties) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(cacheManagerProperties.getDefaultCacheMillis()).disableCachingNullValues();
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        Set cacheNames = new HashSet<>();
        Map cacheConfig = new HashMap<>();
        if (null != cacheManagerProperties.getCaches()) {
            for (Map.Entry entry : cacheManagerProperties.getCaches().entrySet()) {
                cacheNames.add(entry.getKey());
                cacheConfig.put(entry.getKey(), redisCacheConfiguration.entryTtl(entry.getValue()));
            }
        }
        if(!JedisConfig.getJedisConfig().isCusCache())
            return RedisCacheManager.builder(redisCacheWriter)
                    .cacheDefaults(redisCacheConfiguration)
                    .initialCacheNames(cacheNames)
                    .withInitialCacheConfigurations(cacheConfig)
                    .build();
        ObjectMapper objectMapper = new ObjectMapper();
        redisCacheConfiguration = redisCacheConfiguration
            .computePrefixWith(cacheName -> (getPrefix().endsWith(":") ? getPrefix() : getPrefix().concat(":")).concat(cacheName).concat(":"))
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(createJackson2JsonRedisSerializer(objectMapper)));
                return new CustomizedRedisCacheManager(redisCacheWriter, redisCacheConfiguration, cacheConfig);
    }

    /**
     * 缓存的序列化
     *
     * @param
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        ObjectMapper objectMapper = new ObjectMapper();
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(60 * 60))
                .disableCachingNullValues()
                .computePrefixWith(cacheName -> (getPrefix().endsWith(":") ? getPrefix() : getPrefix().concat(":")).concat(cacheName).concat(":"))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(createJackson2JsonRedisSerializer(objectMapper)));
        return new CustomizedRedisCacheManager(redisConnectionFactory, cacheConfiguration);
        //改变CacheManager的创建方式,原方式如下
        // return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();
    }

    private RedisSerializer createJackson2JsonRedisSerializer(ObjectMapper objectMapper) {
        // TODO Auto-generated method stub\
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 解决jackson2无法反序列化LocalDateTime的问题
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(keySerializer());
        redisTemplate.setHashKeySerializer(keySerializer());
        redisTemplate.setValueSerializer(valueSerializer());
        redisTemplate.setHashValueSerializer(valueSerializer());
        return redisTemplate;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
        stringRedisTemplate.setKeySerializer(keySerializer());
        return stringRedisTemplate;
    }


    /**
     * 用于spring session,防止每次创建一个线程
     * @return
     */
    @Bean
    public ThreadPoolTaskExecutor springSessionRedisTaskExecutor() {
        ThreadPoolTaskExecutor springSessionRedisTaskExecutor = new ThreadPoolTaskExecutor();
        springSessionRedisTaskExecutor.setCorePoolSize(8);
        springSessionRedisTaskExecutor.setMaxPoolSize(16);
        springSessionRedisTaskExecutor.setKeepAliveSeconds(10);
        springSessionRedisTaskExecutor.setQueueCapacity(1000);
        springSessionRedisTaskExecutor.setThreadNamePrefix("Spring session redis executor thread: ");
        return springSessionRedisTaskExecutor;
    }


    private RedisSerializer keySerializer() {
        return new StringRedisSerializer();
    }

    private RedisSerializer valueSerializer() {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 解决jackson2无法反序列化LocalDateTime的问题
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer/*new GenericJackson2JsonRedisSerializer()*/;
    }
}