com.lingdonge.redis.util.RedisConnUtil Maven / Gradle / Ivy
package com.lingdonge.redis.util;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
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.lingdonge.core.reflect.OptionalUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.interceptor.KeyGenerator;
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.*;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisPoolConfig;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
/**
* Redis配置生成类
*/
public class RedisConnUtil {
/**
* 取出Cluster里面的节点
*
* @param redisProperties
* @return
*/
public static Set getClusterNodes(RedisProperties redisProperties) {
Set nodeSet = new HashSet<>();
for (String node : redisProperties.getCluster().getNodes()) {
String[] split = node.split(":");
nodeSet.add(new HostAndPort(split[0], Integer.valueOf(split[1])));
}
return nodeSet;
}
/**
* @param redisProperties
* @return
*/
public static Set getClusterRedisNodes(RedisProperties redisProperties) {
Set nodeSet = new HashSet<>();
for (String node : redisProperties.getCluster().getNodes()) {
String[] split = node.split(":");
nodeSet.add(new RedisNode(split[0], Integer.valueOf(split[1])));
}
return nodeSet;
}
/**
* 取出Sentinel哨兵里面的节点
*
* @param redisProperties
* @return
*/
public static Set getSentinelNodes(RedisProperties redisProperties) {
Set nodeSet = new HashSet<>();
for (String node : redisProperties.getSentinel().getNodes()) {
String[] split = node.split(":");
nodeSet.add(new HostAndPort(split[0], Integer.valueOf(split[1])));
}
return nodeSet;
}
/**
* 取出Sentinel哨兵里面的节点
*
* @param redisProperties
* @return
*/
public static Set getSentinelRedisNodes(RedisProperties redisProperties) {
Set nodeSet = new HashSet<>();
for (String node : redisProperties.getSentinel().getNodes()) {
String[] split = node.split(":");
nodeSet.add(new RedisNode(split[0], Integer.valueOf(split[1])));
}
return nodeSet;
}
/**
* Redis哨兵模式配置明细
* 不要使用Bean,防止直接创建。这里根据配置文件来建立
*
* @return
*/
public static RedisSentinelConfiguration buildRedisSentinelConfiguration(RedisProperties properties) {
RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
configuration.setSentinels(getSentinelRedisNodes(properties));
configuration.setMaster(properties.getSentinel().getMaster());
return configuration;
}
/**
* Cluster集群配置生成
*
* @param redisProperties
* @return
*/
public static RedisClusterConfiguration buildRedisClusterConfiguration(RedisProperties redisProperties) {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
// 配置集群密码和节点
redisClusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
redisClusterConfiguration.setClusterNodes(getClusterRedisNodes(redisProperties));
redisClusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
return redisClusterConfiguration;
}
/**
* 生成Lettuce的配置
*
* @param redisProperties
* @return
*/
public static GenericObjectPoolConfig getLettucePoolConfig(RedisProperties redisProperties) {
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
if (null != redisProperties.getLettuce() && null != redisProperties.getLettuce().getPool()) {
genericObjectPoolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
genericObjectPoolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
genericObjectPoolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
genericObjectPoolConfig.setMaxWaitMillis(redisProperties.getLettuce().getPool().getMaxWait().toMillis());
} else {
RedisProperties.Pool pool = new RedisProperties.Pool();
genericObjectPoolConfig.setMaxIdle(pool.getMaxIdle());
genericObjectPoolConfig.setMinIdle(pool.getMinIdle());
genericObjectPoolConfig.setMaxTotal(pool.getMaxActive());
genericObjectPoolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
}
return genericObjectPoolConfig;
}
/**
* 生成Jedis的 GenericObjectPoolConfig 配置
*
* @param redisProperties
* @return
*/
public static GenericObjectPoolConfig getJedisPoolGenericConfig(RedisProperties redisProperties) {
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
if (null != redisProperties.getJedis() && null != redisProperties.getJedis().getPool()) {
genericObjectPoolConfig.setMaxIdle(redisProperties.getJedis().getPool().getMaxIdle());
genericObjectPoolConfig.setMinIdle(redisProperties.getJedis().getPool().getMinIdle());
genericObjectPoolConfig.setMaxTotal(redisProperties.getJedis().getPool().getMaxActive());
genericObjectPoolConfig.setMaxWaitMillis(redisProperties.getJedis().getPool().getMaxWait().toMillis());
} else { // 没有配置使用默认设置
RedisProperties.Pool pool = new RedisProperties.Pool();
genericObjectPoolConfig.setMaxIdle(pool.getMaxIdle());
genericObjectPoolConfig.setMinIdle(pool.getMinIdle());
genericObjectPoolConfig.setMaxTotal(pool.getMaxActive());
genericObjectPoolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
}
return genericObjectPoolConfig;
}
/**
* 创建Jedis连接池配置
*
* @return
*/
// @Bean
public static JedisPoolConfig getJedisPoolConfig(RedisProperties properties) {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
jedisPoolConfig.setBlockWhenExhausted(true);
// 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
jedisPoolConfig.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
// 是否启用pool的jmx管理功能, 默认true
jedisPoolConfig.setJmxEnabled(true);
jedisPoolConfig.setJmxNamePrefix("pool");
// 是否启用后进先出, 默认true
jedisPoolConfig.setLifo(true);
// Spring 1.x
// jedisPoolConfig.setMaxTotal(properties.getPool().getMaxActive());// 最大连接数, 默认8个,控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;
// jedisPoolConfig.setMaxIdle(properties.getPool().getMaxIdle());// 最大空闲连接数, 默认8个,控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
// jedisPoolConfig.setMaxWaitMillis(properties.getPool().getMaxWait()); //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
// Spring 2.x
if (OptionalUtil.resolve(() -> properties.getJedis().getPool().getMaxActive()).isPresent()) {
jedisPoolConfig.setMaxTotal(properties.getJedis().getPool().getMaxActive());// 最大连接数, 默认8个,控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;
}
if (OptionalUtil.resolve(() -> properties.getJedis().getPool().getMaxIdle()).isPresent()) {
jedisPoolConfig.setMaxIdle(properties.getJedis().getPool().getMaxIdle());// 最大空闲连接数, 默认8个,控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
}
if (OptionalUtil.resolve(() -> properties.getJedis().getPool().getMaxWait()).isPresent()) {
jedisPoolConfig.setMaxWaitMillis(properties.getJedis().getPool().getMaxWait().toMillis()); //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
}
// 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
jedisPoolConfig.setMinEvictableIdleTimeMillis(1800000);
// 最小空闲连接数, 默认0
// jedisPoolConfig.setMinIdle(properties.getPool().getMinIdle());
if (OptionalUtil.resolve(() -> properties.getJedis().getPool().getMinIdle()).isPresent()) {
jedisPoolConfig.setMinIdle(properties.getJedis().getPool().getMinIdle());
}
// 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
jedisPoolConfig.setNumTestsPerEvictionRun(3);
// 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
jedisPoolConfig.setSoftMinEvictableIdleTimeMillis(1800000);
jedisPoolConfig.setTestOnBorrow(false); // 使用时进行扫描,确保都可用,在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;,在获取连接的时候检查有效性, 默认false
jedisPoolConfig.setTestWhileIdle(false);// 在空闲时检查有效性, 默认false,Idle时进行连接扫描
jedisPoolConfig.setTestOnReturn(true); // 还回线程池时进行扫描
// 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(-1);
return jedisPoolConfig;
}
/**
* 根据配置生成RedisTemplate
*
* @param redisProperties
* @return
*/
public static RedisTemplate getRedisTemplateFromJedis(RedisProperties redisProperties) {
JedisConnectionFactory jedisConnectionFactory = getJedisConnectionFactory(redisProperties);
return getRedisTemplate(jedisConnectionFactory);
}
public static StringRedisTemplate getStringRedisTemplateFromJedis(RedisProperties redisProperties) {
JedisConnectionFactory jedisConnectionFactory = getJedisConnectionFactory(redisProperties);
return getStringRedisTemplate(jedisConnectionFactory);
}
/**
* 根据配置生成Lettuce的RedisTemplate
*
* @param redisProperties
* @return
*/
public static RedisTemplate getRedisTemplateFromLettuce(RedisProperties redisProperties) {
LettuceConnectionFactory lettuceConnectionFactory = getLettuceConnectionFactory(redisProperties);
return getRedisTemplate(lettuceConnectionFactory);
}
public static StringRedisTemplate getStringRedisTemplateFromLettuce(RedisProperties redisProperties) {
LettuceConnectionFactory lettuceConnectionFactory = getLettuceConnectionFactory(redisProperties);
return getStringRedisTemplate(lettuceConnectionFactory);
}
/**
* 从配置文件中创建Redis模板
*
* @param redisProperties
* @return
*/
public static RedisTemplate getRedisTemplate(RedisProperties redisProperties) {
if (redisProperties.getLettuce() != null) {
return getRedisTemplateFromLettuce(redisProperties);
}
return getRedisTemplateFromJedis(redisProperties);
}
/**
* 生成StringRedisTemplate
*
* @param redisProperties
* @return
*/
public static StringRedisTemplate getStringRedisTemplate(RedisProperties redisProperties) {
if (redisProperties.getLettuce() != null) {
return getStringRedisTemplateFromLettuce(redisProperties);
}
return getStringRedisTemplateFromJedis(redisProperties);
}
/**
* 根据连接池生成RedisTemplate
* 适配Jedis和Lettuce
*
* Spring Data Redis提供了两个模板:
* 1、RedisTemplate
* 2、StringRedisTemplate,如果key和value都是String类型,建议使用StringRedisTemplate
*
* Spring data redis提供多个序列化器
* GenericToStringSerializer:使用Spring转换服务进行序列化;
* JacksonJsonRedisSerializer:使用Jackson 1,将对象序列化为JSON;
* Jackson2JsonRedisSerializer:使用Jackson 2,将对象序列化为JSON;
* JdkSerializationRedisSerializer:使用Java序列化;
* OxmSerializer:使用Spring O/X映射的编排器和解排器(marshaler和unmarshaler)实现序列化,用于XML序列化;
* StringRedisSerializer:序列化String类型的key和value。
*
* RedisTemplate会默认使用JdkSerializationRedisSerializer,这意味着key和value都会通过Java进行序列化。
* StringRedisTemplate默认会使用StringRedisSerializer
*
* @param redisConnectionFactory
* @return
*/
public static RedisTemplate getRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 方式一:String格式的
// RedisTemplate redisTemplate = new RedisTemplate();
// redisTemplate.setConnectionFactory(factory);
// 方式二:可扩展的
RedisTemplate redisTemplate = new RedisTemplate();
// 方式三:StringTemplate
// StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory); // 配置连接池
// 设置序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = getJackson2JsonSerializer();
// FastJsonRedisSerializer