目录
在springCloud项目中,在接收第三方接口数据的时候发生数据重复性问题。经过分析原因是在相同时间内秒级(日志只记录到秒),第三方发送了相同的数据,导致前面的事务还没有提交的情况下,后面的事务验证信息重复产生异常,导致的数据重复。解决方案是添加分布式锁来确保消息的顺序性。项目github地址
一、使用redis来实现分布式锁。
1、pom文件引入redis相关依赖
引入依赖信息
<!-- redis 不需要version信息 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
2、在配置文件中添加配置
配置连接信息
---
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
jedis:
pool:
enabled: true
max-active: 8
max-idle: 8
min-idle: 1
max-wait: 30000
# password: 123456
3、相关配置类
redis相关的配置类如下图:
FastJson2JsonRedisSerializer是reids使用fastjson 实现序列化
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
/**
* Redis使用FastJson序列化
* @author mengw
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
@SuppressWarnings("unused")
private ObjectMapper objectMapper = new ObjectMapper();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
public FastJson2JsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}
redisUtils是redis基础操作的工具类
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import redis.clients.jedis.commands.JedisCommands;
import redis.clients.jedis.commands.MultiKeyCommands;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author mengw
* @description: redis工具类
**/
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public boolean expire(String key, long time) {
try {
if (time > 0L) {
this.redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception var5) {
var5.printStackTrace();
return false;
}
}
public boolean expire(String key, long time,TimeUnit timeUnit) {
try {
if (time > 0L) {
this.redisTemplate.expire(key, time, timeUnit);
}
return true;
} catch (Exception var5) {
var5.printStackTrace();
return false;
}
}
public long getExpire(String key) {
return this.redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
public boolean hasKey(String key) {
try {
return this.redisTemplate.hasKey(key);
} catch (Exception var3) {
var3.printStackTrace();
return false;
}
}
/**
* 根据key删除
*
* @param key
*/
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
this.redisTemplate.delete(key[0]);
} else {
this.redisTemplate.delete(CollUtil.toList(key));
}
}
}
/**
* 模糊删除key
*
* @param prex key的前缀
*/
public void delKeys(String prex) {
Set<String> keys = redisTemplate.keys(prex);
if (keys != null && keys.size() > 0) {
redisTemplate.delete(keys);
}
}
/**
* 根据key 获取信息
*
* @param key key
* @return Object
*/
public Object get(String key) {
return key == null ? null : this.redisTemplate.opsForValue().get(key);
}
/**
* 根据key 获取信息String
*
* @param key
* @return
*/
public String getString(String key) {
if (StringUtils.isBlank(key)) {
return null;
} else {
Object value = this.redisTemplate.opsForValue().get(key);
return value == null ? null : String.valueOf(value);
}
}
public boolean set(String key, Object value) {
try {
this.redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception var4) {
var4.printStackTrace();
return false;
}
}
/**
* 存储redis
*
* @param key
* @param value
* @param time
* @return
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0L) {
this.redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
this.set(key, value);
}
return true;
} catch (Exception var6) {
var6.printStackTrace();
return false;
}
}
/**
* 按时存储redis
*
* @param key
* @param value
* @param time
* @param timeUnit
* @return
*/
public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
try {
if (time > 0L) {
this.redisTemplate.opsForValue().set(key, value, time, timeUnit);
} else {
this.set(key, value);
}
return true;
} catch (Exception var7) {
var7.printStackTrace();
return false;
}
}
public long incr(String key, long delta) {
if (delta < 0L) {
throw new RuntimeException("递增因子必须大于0");
} else {
return this.redisTemplate.opsForValue().increment(key, delta);
}
}
/**
* 自增渠道ID(key-'CH'+tenantId+channelGroupId)
*
* @param key
* @return
*/
public String incrChannelId(String key) {
return "CH" + this.redisTemplate.opsForValue().increment(key, 1L).toString() + "-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss"));
}
public long decr(String key, long delta) {
if (delta < 0L) {
throw new RuntimeException("递减因子必须大于0");
} else {
return this.redisTemplate.opsForValue().increment(key, -delta);
}
}
public Object hget(String key, String item) {
return this.redisTemplate.opsForHash().get(key, item);
}
public Map<Object, Object> hmget(String key) {
return this.redisTemplate.opsForHash().entries(key);
}
public boolean hmset(String key, Map<String, Object> map) {
try {
this.redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception var4) {
var4.printStackTrace();
return false;
}
}
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
this.redisTemplate.opsForHash().putAll(key, map);
if (time > 0L) {
this.expire(key, time);
}
return true;
} catch (Exception var6) {
var6.printStackTrace();
return false;
}
}
/**
* hash格式存储
*
* @param key
* @param item
* @param value
* @return
*/
public boolean hset(String key, String item, Object value) {
try {
this.redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception var5) {
var5.printStackTrace();
return false;
}
}
public boolean hsetAll(String key, Map<Object, Object> map) {
try {
this.redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception var4) {
var4.printStackTrace();
return false;
}
}
/**
* hash 定时存储
*
* @param key
* @param item
* @param value
* @param time
* @return
*/
public boolean hset(String key, String item, Object value, long time) {
try {
this.redisTemplate.opsForHash().put(key, item, value);
if (time > 0L) {
this.expire(key, time);
}
return true;
} catch (Exception var7) {
var7.printStackTrace();
return false;
}
}
/**
* hash 获取size
*
* @param key
* @return
*/
public Long hsize(String key) {
final Long size = this.redisTemplate.opsForHash().size(key);
return size;
}
/**
* hash 删除
*
* @param key
* @param item
*/
public void hdel(String key, Object... item) {
this.redisTemplate.opsForHash().delete(key, item);
}
public boolean hHasKey(String key, String item) {
return this.redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash 自增
*
* @param key
* @param item
* @param by
* @return
*/
public double hincr(String key, String item, double by) {
return this.redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash 自减
*
* @param key
* @param item
* @param by
* @return
*/
public double hdecr(String key, String item, double by) {
return this.redisTemplate.opsForHash().increment(key, item, -by);
}
public Set<Object> sGet(String key) {
try {
return this.redisTemplate.opsForSet().members(key);
} catch (Exception var3) {
var3.printStackTrace();
return null;
}
}
public boolean sHasKey(String key, Object value) {
try {
return this.redisTemplate.opsForSet().isMember(key, value);
} catch (Exception var4) {
var4.printStackTrace();
return false;
}
}
public long sSet(String key, Object... values) {
try {
return this.redisTemplate.opsForSet().add(key, values);
} catch (Exception var4) {
var4.printStackTrace();
return 0L;
}
}
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = this.redisTemplate.opsForSet().add(key, values);
if (time > 0L) {
this.expire(key, time);
}
return count;
} catch (Exception var6) {
var6.printStackTrace();
return 0L;
}
}
public long sGetSetSize(String key) {
try {
return this.redisTemplate.opsForSet().size(key);
} catch (Exception var3) {
var3.printStackTrace();
return 0L;
}
}
public long setRemove(String key, Object... values) {
try {
Long count = this.redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception var4) {
var4.printStackTrace();
return 0L;
}
}
public List<Object> lGet(String key, long start, long end) {
try {
return this.redisTemplate.opsForList().range(key, start, end);
} catch (Exception var7) {
var7.printStackTrace();
return null;
}
}
public long lGetListSize(String key) {
try {
return this.redisTemplate.opsForList().size(key);
} catch (Exception var3) {
var3.printStackTrace();
return 0L;
}
}
public Object lGetIndex(String key, long index) {
try {
return this.redisTemplate.opsForList().index(key, index);
} catch (Exception var5) {
var5.printStackTrace();
return null;
}
}
public boolean lSet(String key, Object value) {
try {
this.redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception var4) {
var4.printStackTrace();
return false;
}
}
public boolean lSet(String key, Object value, long time) {
try {
this.redisTemplate.opsForList().rightPush(key, value);
if (time > 0L) {
this.expire(key, time);
}
return true;
} catch (Exception var6) {
var6.printStackTrace();
return false;
}
}
public boolean lSet(String key, List<Object> value) {
try {
this.redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception var4) {
var4.printStackTrace();
return false;
}
}
public boolean lSet(String key, List<Object> value, long time) {
try {
this.redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0L) {
this.expire(key, time);
}
return true;
} catch (Exception var6) {
var6.printStackTrace();
return false;
}
}
public boolean lUpdateIndex(String key, long index, Object value) {
try {
this.redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception var6) {
var6.printStackTrace();
return false;
}
}
public long lRemove(String key, long count, Object value) {
try {
Long remove = this.redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception var6) {
var6.printStackTrace();
return 0L;
}
}
/**
* 从指定队列里取得数据
*
* @param key
* @param size
* @return
*/
public List<Object> getFromQueue(String key, long size) {
ListOperations<String, Object> stringObjectListOperations = redisTemplate.opsForList();
if (size > 0) {
return stringObjectListOperations.range(key, 0, size - 1);
} else {
return stringObjectListOperations.range(key, 0, stringObjectListOperations.size(key) - 1);
}
}
/**
* 存到指定的队列中
*
* @param key
* @param val
* @param size
*/
public Long saveToQueue(String key, String val, long size) {
ListOperations<String, Object> lo = redisTemplate.opsForList();
if (size > 0 && lo.size(key) >= size) {
lo.rightPop(key);
}
return lo.leftPush(key, val);
}
/**
* 获取hash表中全部K-V数据
*
* @param key
*/
public Map<Object, Object> hgetAll(String key) {
Map<Object, Object> result = redisTemplate.opsForHash().entries(key);
return result;
}
public Set<String> keys(String keyprefix) {
Set<String> keySet = redisTemplate.keys(keyprefix + "*");
return keySet;
}
public Set<String> scan(String matchKey) {
Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> keysTmp = new HashSet<>();
Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(matchKey + "*").count(100000).build());
while (cursor.hasNext()) {
keysTmp.add(new String(cursor.next()));
}
return keysTmp;
});
return keys;
}
/**
*
* @param key scan 操作key
* @return set
*/
public Set<String> scan1(String key) {
return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> keys = new HashSet<>();
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
MultiKeyCommands multiKeyCommands = (MultiKeyCommands) commands;
ScanParams scanParams = new ScanParams();
scanParams.match(key);
scanParams.count(1000);
ScanResult<String> scan = multiKeyCommands.scan("0", scanParams);
while (null != scan.getCursor()) {
keys.addAll(scan.getResult());
if (!StringUtils.equals("0", scan.getCursor())) {
scan = multiKeyCommands.scan(scan.getCursor(), scanParams);
continue;
} else {
break;
}
}
return keys;
});
}
/**
* 不设置过期时长
*/
private final static long NOT_EXPIRE = -1;
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key, Class<T> clazz) {
ValueOperations<String, Object> operation = redisTemplate.opsForValue();
T t = (T) operation.get(key);
if (t == null) {
return null;
}
if (clazz == String.class) {
return t;
}
return fromJson(t.toString(), clazz);
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value) {
redisTemplate.opsForValue().set(key, toJson(value));
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, toJson(value), timeout, timeUnit);
}
/**
* Object转成JSON数据
*/
private String toJson(Object object) {
if (object instanceof Integer || object instanceof Long || object instanceof Float || object instanceof Double
|| object instanceof Boolean || object instanceof String) {
return String.valueOf(object);
}
return JSON.toJSONString(object);
}
/**
* JSON数据,转成Object
*/
private <T> T fromJson(String json, Class<T> clazz) {
return JSONObject.parseObject(json,clazz);
}
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection) {
return redisTemplate.delete(collection);
}
public RedisUtil(final RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
}
分布式锁的接口
import java.util.concurrent.TimeUnit;
public interface Locker {
/**
* 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。
*
* @param lockKey
*/
void lock(String lockKey);
/**
* 释放锁
*
* @param lockKey
*/
void unlock(String lockKey);
/**
* 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。如果获取到锁后,执行结束后解锁或达到超时时间后会自动释放锁
*
* @param lockKey
* @param timeout
*/
void lock(String lockKey, int timeout);
/**
* 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。如果获取到锁后,执行结束后解锁或达到超时时间后会自动释放锁
*
* @param lockKey
* @param unit
* @param timeout
*/
void lock(String lockKey, TimeUnit unit, int timeout);
/**
* 尝试获取锁,获取到立即返回true,未获取到立即返回false
*
* @param lockKey
* @return
*/
boolean tryLock(String lockKey);
/**
* 尝试获取锁,在等待时间内获取到锁则返回true,否则返回false,如果获取到锁,则要么执行完后程序释放锁,
* 要么在给定的超时时间leaseTime后释放锁
*
* @param lockKey
* @param waitTime
* @param leaseTime
* @param unit
* @return
*/
boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit)
throws InterruptedException;
/**
* 锁是否被任意一个线程锁持有
*
* @param lockKey
* @return
*/
boolean isLocked(String lockKey);
}
Lock接口是的实现类RedissonLocker,使用RedisSession实现加锁开锁。
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: mengw
**/
public class RedissonLocker implements Locker {
private RedissonClient redissonClient;
public RedissonLocker(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
/**
* 没有超时时间,默认30s
*
* @param lockKey
* @return
*/
@Override
public void lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
try {
lock.unlock();
} catch (Exception e) {
if (lock.isLocked()) {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
/**
* 自己设置超时时间
*
* @param lockKey 锁的key
* @param leaseTime 秒 如果是-1,直到自己解锁,否则不会自动解锁
* @return
*/
@Override
public void lock(String lockKey, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
}
@Override
public void lock(String lockKey, TimeUnit unit, int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
}
public void setRedissonClient(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
@Override
public boolean tryLock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock();
}
/**
* @param lockKey 锁key
* @param waitTime 等到最大时间,强制获取锁
* @param leaseTime 锁失效时间
* @param unit 锁单位
* @return
* @throws InterruptedException
*/
@Override
public boolean tryLock(String lockKey, long waitTime, long leaseTime,
TimeUnit unit) throws InterruptedException {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock(waitTime, leaseTime, unit);
}
@Override
public boolean isLocked(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.isLocked();
}
}
LockerUtil是加锁解锁的工具类
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: mengw
**/
public final class LockerUtil {
private static Locker locker;
/**
* 设置工具类使用的locker
*
* @param locker
*/
public static void setLocker(Locker locker) {
LockerUtil.locker = locker;
}
/**
* 获取锁
*
* @param lockKey
*/
public static void lock(String lockKey) {
locker.lock(lockKey);
}
/**
* 释放锁
*
* @param lockKey
*/
public static void unlock(String lockKey) {
locker.unlock(lockKey);
}
/**
* 获取锁,超时释放
*
* @param lockKey
* @param timeout
*/
public static void lock(String lockKey, int timeout) {
locker.lock(lockKey, timeout);
}
/**
* 获取锁,超时释放,指定时间单位
*
* @param lockKey
* @param unit
* @param timeout
*/
public static void lock(String lockKey, TimeUnit unit, int timeout) {
locker.lock(lockKey, unit, timeout);
}
/**
* 尝试获取锁,获取到立即返回true,获取失败立即返回false
*
* @param lockKey
* @return
*/
public static boolean tryLock(String lockKey) {
return locker.tryLock(lockKey);
}
/**
* 尝试获取锁,在给定的waitTime时间内尝试,获取到返回true,获取失败返回false,获取到后再给定的leaseTime时间超时释放
*
* @param lockKey
* @param waitTime
* @param leaseTime
* @param unit
* @return
* @throws InterruptedException
*/
public static boolean tryLock(String lockKey, long waitTime, long leaseTime,
TimeUnit unit){
try {
return locker.tryLock(lockKey, waitTime, leaseTime, unit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 锁释放被任意一个线程持有
*
* @param lockKey
* @return
*/
public static boolean isLocked(String lockKey) {
return locker.isLocked(lockKey);
}
}
RedisTemplateConfiguration是具体配置类,实现RedisUtil、RedisSession以及相关类的注册配置。
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.jsontype.impl.LaissezFaireSubTypeValidator;
import com.ufc.dream.web_start.config.redisession.LockerUtil;
import com.ufc.dream.web_start.config.redisession.RedissonLocker;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
/**
* @description: redis配置类
* @author: mengw
**/
@EnableCaching
@Configuration
@AutoConfigureBefore({RedisAutoConfiguration.class})
public class RedisTemplateConfiguration {
/**
* @description: 获取redis 相关的配置信息
*/
@Autowired
private RedisProperties redisProperties;
public RedisTemplateConfiguration() {
}
@Bean
@ConditionalOnMissingBean({RedisSerializer.class})
public StringRedisSerializer redisSerializer() {
return new StringRedisSerializer();
}
@Bean(name = {"redisTemplate"})
@ConditionalOnMissingBean({RedisTemplate.class})
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1L));
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(factory)).cacheDefaults(redisCacheConfiguration).build();
}
/**
* 注册 RedisConnectionFactory
* @param jedisPoolConfig
* @param redisStandaloneConfiguration
* @return
*/
@Bean
public RedisConnectionFactory factory(JedisPoolConfig jedisPoolConfig, RedisStandaloneConfiguration redisStandaloneConfiguration) {
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
connectionFactory.setPoolConfig(jedisPoolConfig);
return connectionFactory;
}
@Bean
public RedisStandaloneConfiguration redisStandaloneConfiguration() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(redisProperties.getHost());
config.setPort(redisProperties.getPort());
config.setDatabase(redisProperties.getDatabase());
config.setPassword(RedisPassword.of(redisProperties.getPassword()));
return config;
}
@Bean
public JedisPoolConfig jedisPoolConfig() {
RedisProperties.Pool pool = redisProperties.getJedis().getPool();
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(pool.getMaxActive());
jedisPoolConfig.setMaxIdle(pool.getMaxIdle());
jedisPoolConfig.setMinIdle(pool.getMinIdle());
jedisPoolConfig.setMaxWait(pool.getMaxWait());
return jedisPoolConfig;
}
@Bean
public JedisPool jedisPool(JedisPoolConfig jedisPoolConfig) {
return new JedisPool(jedisPoolConfig, redisProperties.getHost(),
redisProperties.getPort(), 0, redisProperties.getPassword(), redisProperties.getDatabase());
}
/**
* 注册RedisUtil
* @param redisTemplate
* @return
*/
@Bean(name = {"redisUtil"})
@ConditionalOnBean({RedisTemplate.class})
public RedisUtil redisUtils(RedisTemplate<String, Object> redisTemplate) {
return new RedisUtil(redisTemplate);
}
@Bean
public RedissonLocker redissonLocker(RedissonClient redissonClient) {
RedissonLocker redissonLocker = new RedissonLocker(redissonClient);
// LockerUtils locker 实例化
LockerUtil.setLocker(redissonLocker);
return redissonLocker;
}
/**
* @description 注册 RedissonClient RedissonLocker类注册时 LockerUtils locker 实例化
* @return
*/
@Bean
public RedissonClient redissonClient() {
Config cfg = new Config();
String redisUrl = String.format("redis://%s:%s", redisProperties.getHost() + "", redisProperties.getPort() + "");
cfg.useSingleServer().setAddress(redisUrl);
// .setPassword(redisProperties.getPassword());
cfg.useSingleServer().setDatabase(redisProperties.getDatabase());
cfg.useSingleServer().setConnectTimeout(3000)
.setTimeout(3000)
.setRetryAttempts(3)
.setRetryInterval(1500)
.setPingConnectionInterval(3000)//**此项务必设置为redisson解决之前bug的timeout问题关键*****
;
return Redisson.create(cfg);
}
}
4、分布式锁的使用
@SpringBootTest
public class TestRedisService {
@Autowired
private RedisUtil redisUtil;
@Test
public void test(){
redisUtil.set("test", "test");
String test = redisUtil.getString("test");
try {
boolean hasLock = LockerUtil.tryLock("RedisLocal", 15, 15, TimeUnit.SECONDS);
if (hasLock) {
System.out.println(" 加锁成功");
}
}finally {
LockerUtil.unlock("RedisLocal");
System.out.println("解锁完成");
}
}
}