@Configuration
@ConditionalOnClass({GenericObjectPool.class})
@Slf4j
public class RedisConfig{
@Autowired
RedisProperties redisProperties;
@Value("${spring.redis.strategy}")
private String strategy;
@Bean
public JedisSentinelPool jedisSentinelPool(JedisPoolConfig jedisPoolConfig){
JedisSentinelPool jedisSentinelPool = null;
if (RedisUtils.SENTINEL == Integer.parseInt(strategy)) {
Set<String> sentinels = new HashSet(Arrays.asList(redisProperties.getSentinel().getNodes()));
jedisSentinelPool = new JedisSentinelPool(redisProperties.getSentinel().getMaster(), sentinels,
jedisPoolConfig,redisProperties.getPassword());
}
return jedisSentinelPool;
}
@Bean
@ConditionalOnMissingBean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大连接数
jedisPoolConfig.setMaxTotal(redisProperties.getPool() == null ? 100 : redisProperties.getPool().getMaxIdle());
//最小空闲连接数
jedisPoolConfig.setMinIdle(redisProperties.getPool() == null ? 20 : redisProperties.getPool().getMinIdle());
//当池内没有可用连接时,最大等待时间
jedisPoolConfig.setMaxWaitMillis(redisProperties.getPool() == null ? 10000 : redisProperties.getPool().getMaxWait());
return jedisPoolConfig;
}
//Redis 单点
@Bean
@ConditionalOnProperty(name="spring.redis.strategy", havingValue = "1")
public JedisConnectionFactory getRedisFactory(JedisPoolConfig jedisPoolConfig) {
log.info("load {}", "getRedisFactory");
JedisConnectionFactory factory = new JedisConnectionFactory(jedisPoolConfig);
factory.setHostName(redisProperties.getHost());
factory.setPort(redisProperties.getPort());
if (StringUtils.isNotBlank(redisProperties.getPassword())) {
factory.setPassword(redisProperties.getPassword());
}
factory.setUsePool(true);
return factory;
}
//Redis 集群配置
@Bean
@ConditionalOnProperty(name="spring.redis.strategy", havingValue = "2")
public JedisConnectionFactory getClusterRedisFactory(JedisPoolConfig jedisPoolConfig) {
JedisConnectionFactory jedisConnectionFactory;
log.info("load {}", "getClusterRedisFactory");
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
for (String s : redisProperties.getCluster().getNodes()) {
String[] split1 = s.split(":");
redisClusterConfiguration.addClusterNode(new RedisNode(split1[0], Integer.parseInt(split1[1])));
}
//集群时最大重定向个数
redisClusterConfiguration.setMaxRedirects(5);
jedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration, jedisPoolConfig );
if (StringUtils.isNotBlank(redisProperties.getPassword())) {
jedisConnectionFactory.setPassword(redisProperties.getPassword());
}
return jedisConnectionFactory;
}
//Redis 哨兵
@Bean
@ConditionalOnProperty(name="spring.redis.strategy", havingValue = "3")
public JedisConnectionFactory getSentinelRedisFactory(JedisPoolConfig jedisPoolConfig ) {
JedisConnectionFactory jedisConnectionFactory;
log.info("load {}", "getSentinelRedisFactory");
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
redisSentinelConfiguration.setMaster(redisProperties.getSentinel().getMaster());
String[] sentinelArray = org.springframework.util.StringUtils
.commaDelimitedListToStringArray(redisProperties.getSentinel().getNodes());
for (String s : sentinelArray) {
try {
String[] split1 = s.split(":");
redisSentinelConfiguration.addSentinel(new RedisNode(split1[0], Integer.parseInt(split1[1])));
} catch (Exception e) {
throw new RuntimeException(String.format("出现配置错误!请确认node=[%s]是否正确", s));
}
}
jedisConnectionFactory = new JedisConnectionFactory(redisSentinelConfiguration, jedisPoolConfig);
if (StringUtils.isNotBlank(redisProperties.getPassword())) {
jedisConnectionFactory.setPassword(redisProperties.getPassword());
}
return jedisConnectionFactory;
}
/*
装配
*/
@Bean("redisTemplate")
// @RefreshScope
public <T extends Object> RedisTemplate<String, T> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
RedisTemplate<String, T> redisTmplate = new RedisTemplate<String, T>();
redisTmplate.setConnectionFactory(jedisConnectionFactory);
redisTmplate.setKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
redisTmplate.setValueSerializer(jackson2JsonRedisSerializer);
//redisTmplate.setHashKeySerializer(new StringRedisSerializer());
redisTmplate.setHashKeySerializer(jackson2JsonRedisSerializer);
redisTmplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTmplate;
}
@Bean
public RedisLock redisLock(JedisConnectionFactory jedisConnectionFactory) {
RedisLock redisLock = new RedisLock.Builder(jedisConnectionFactory, RedisUtil.STANDALONE)
.lockPrefix("lock_")
.sleepTime(50)
.build();
return redisLock;
}
}
@Slf4j
public class RedisLock {
private static final String LOCK_MSG = "OK";
private static final Long UNLOCK_MSG = 1L;
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private String lockPrefix;
private int sleepTime;
private JedisConnectionFactory jedisConnectionFactory;
@Autowired
private JedisSentinelPool jedisSentinelPool;
private int type;
/**
* time millisecond
*/
private static final int TIME = 1000;
/**
* lua script
*/
private String script;
private RedisLock(Builder builder) {
this.jedisConnectionFactory = builder.jedisConnectionFactory;
this.type = builder.type;
this.lockPrefix = builder.lockPrefix;
this.sleepTime = builder.sleepTime;
buildScript();
}
/**
* get Redis connection
*
* @return
*/
private Object getConnection() {
Object connection;
if (RedisUtil.STANDALONE == type) {
RedisConnection redisConnection = jedisConnectionFactory.getConnection();
connection = redisConnection.getNativeConnection();
} else if (RedisUtil.CLUSTER == type) {
RedisClusterConnection redisConnection = jedisConnectionFactory.getClusterConnection();
connection = redisConnection.getNativeConnection();
} else {
connection = jedisSentinelPool.getResource();
}
return connection;
}
/**
* Non-blocking lock
*
* @param key
* @return
*/
public boolean tryLock(String key) {
return tryLock(key, 10 * TIME);
}
/**
* Non-blocking lock
*
* @param key
* @param timeout
* @return
*/
public boolean tryLock(String key, int timeout) {
return tryLock(key, UUID.randomUUID().toString(), timeout);
}
/**
* Non-blocking lock
*
* @param key lock business type
* @param request value
* @return true lock success
* false lock fail
*/
public boolean tryLock(String key, String request) {
return tryLock(key, request, 10 * TIME);
}
/**
* Non-blocking lock
*
* @param key lock business type
* @param request value
* @param expireTime custom expireTime,by millseconds
* @return true lock success
* false lock fail
*/
public boolean tryLock(String key, String request, int expireTime) {
//get connection
Object connection = getConnection();
String result = "";
if (connection instanceof Jedis) {
try {
result = ((Jedis) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
} catch (Exception e) {
log.error("tryLock()方法,key:{},redis分布式锁异常信息:", key, e);
} finally {
((Jedis) connection).close();
}
} else {
result = ((JedisCluster) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
}
return LOCK_MSG.equals(result);
}
/**
* blocking lock
*
* @param key
* @param request
*/
public void lock(String key, String request) throws InterruptedException {
//get connection
Object connection = getConnection();
String result = "";
for (; ; ) {
if (connection instanceof Jedis) {
try {
result = ((Jedis) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
} catch (Exception e) {
log.error("lock()方法,key:{},redis分布式锁异常信息:", key, e);
((Jedis) connection).close();
} finally {
if (LOCK_MSG.equals(result)) {
((Jedis) connection).close();
}
}
} else {
result = ((JedisCluster) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
}
if (LOCK_MSG.equals(result)) {
break;
}
Thread.sleep(sleepTime);
}
}
/**
* blocking lock,custom time
*
* @param key
* @param request
* @param blockTime custom time
* @return
* @throws InterruptedException
*/
public boolean lock(String key, String request, int blockTime) throws InterruptedException {
//get connection
Object connection = getConnection();
String result = "";
while (blockTime >= 0) {
if (connection instanceof Jedis) {
try {
result = ((Jedis) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
} catch (Exception e) {
log.error("lock()方法,key:{},redis分布式锁异常信息:", key, e);
((Jedis) connection).close();
} finally {
if (LOCK_MSG.equals(result)) {
((Jedis) connection).close();
}
}
} else {
result = ((JedisCluster) connection).set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
}
if (LOCK_MSG.equals(result)) {
return true;
}
blockTime -= sleepTime;
Thread.sleep(sleepTime);
}
return false;
}
/**
* unlock
*
* @param key
* @param request request must be the same as lock request
* @return
*/
public boolean unlock(String key, String request) {
//get connection
Object connection = getConnection();
//lua script
Object result = null;
if (connection instanceof Jedis) {
try {
result = ((Jedis) connection).eval(script, Collections.singletonList(lockPrefix + key), Collections.singletonList(request));
} catch (Exception e) {
log.error("unlock()方法,key:{},redis分布式锁异常信息:", key, e);
} finally {
((Jedis) connection).close();
}
} else if (connection instanceof JedisCluster) {
result = ((JedisCluster) connection).eval(script, Collections.singletonList(lockPrefix + key), Collections.singletonList(request));
} else {
//throw new RuntimeException("instance is error") ;
return false;
}
return UNLOCK_MSG.equals(result);
}
/**
* read lua script
*/
private void buildScript() {
script = RedisUtil.getScript("lock.lua");
}
public static class Builder {
private static final String DEFAULT_LOCK_PREFIX = "lock_";
/**
* default sleep time
*/
private static final int DEFAULT_SLEEP_TIME = 100;
private JedisConnectionFactory jedisConnectionFactory = null;
private int type;
private String lockPrefix = DEFAULT_LOCK_PREFIX;
private int sleepTime = DEFAULT_SLEEP_TIME;
public Builder(JedisConnectionFactory jedisConnectionFactory, int type) {
this.jedisConnectionFactory = jedisConnectionFactory;
this.type = type;
}
public Builder lockPrefix(String lockPrefix) {
this.lockPrefix = lockPrefix;
return this;
}
public Builder sleepTime(int sleepTime) {
this.sleepTime = sleepTime;
return this;
}
public RedisLock build() {
return new RedisLock(this);
}
}
}