import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.*; import java.util.concurrent.TimeUnit; @Slf4j @Component public class RedisUtils { private static final ThreadLocal<Map<String, String>> THREAD_LOCAL_LOCK = new ThreadLocal<>(); private static final String LUA_UN_LOCK = "if redis.call('get', KEYS[1]) == ARGV[1] then \n" + " return redis.call('del', KEYS[1]) \n" + "else \n" + " return 0 \n" + "end"; private static final Long LUA_1L = 1L; @Resource private RedisTemplate<String, Object> redisTemplate; /** * 指定缓存失效时间 * * @param key * @param time 时间(秒) * @return */ public boolean expire(String key, long time) { return expire(key, time, false); } public boolean expire(String key, long time, boolean randomExpire) { if (time > 0) { long seconds = randomExpire ? randomTimes(time) : time; return redisTemplate.expire(key, seconds, TimeUnit.SECONDS); } return false; } public boolean expireAt(String key, Date expireTime) { return redisTemplate.expireAt(key, expireTime); } /** * 根据key 获取过期时间 * @param key * @return 时间(秒) 返回0代表为永久有效 */ public Long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 设置键值同时指定过期时间,键值不存在的时候可以设置进去 * * @param key * @param value * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true成功 false失败 */ public boolean setIfAbsent(String key, Object value, long time) { try { return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS); } catch (Exception e) { log.error("redis.setIfAbsent_error fail:{}", e.getMessage(), e); return false; } } /** * 键值递增 * @param key * @param delta 要增加几(大于0) * @return */ public Long incr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递增因子必须大于0"); } return redisTemplate.opsForValue().increment(key, delta); } public Long incr(String key, long delta, long expireSeconds) { //此处不用限制delta的正负 Long result = redisTemplate.opsForValue().increment(key, delta); if (result != null && result == delta) { redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS); } return result; } /** * 键值递减 * * @param key * @param delta 要减少几(小于0) * @return */ public long decr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递减因子必须大于0"); } return redisTemplate.opsForValue().increment(key, -delta); } private long randomTimes(long expireTimes) { return randomTimes(null, expireTimes); } private long randomTimes(Random random, long expireTimes) { if (random == null) { random = new Random(); } return expireTimes + random.nextInt((int) expireTimes); } /** * 加锁 * * @param key * @param waitSeconds * @param unlockSeconds * @return */ public boolean tryLock(String key, long waitSeconds, long unlockSeconds) { // 默认自动解锁时间: 60s if (unlockSeconds <= 0) { unlockSeconds = 60; } String value = UUID.randomUUID().toString(); setThreadLockValue(key, value); long startTime = System.currentTimeMillis(); while (true) { if (setIfAbsent(key, value, unlockSeconds)) { return true; } // 超过等待时间,则加锁失败 if (System.currentTimeMillis() - startTime > waitSeconds * 1000) { return false; } } } /** * 尝试加锁,失败立马返回 * * @param key 锁名称 * @param expireSeconds 超时时长 * @return */ public boolean tryLock(String key, long expireSeconds) { String value = UUID.randomUUID().toString(); setThreadLockValue(key, value); return redisTemplate.opsForValue().setIfAbsent(key, value, expireSeconds, TimeUnit.SECONDS); } /** * 解锁 * * @param key */ public boolean unlock(String key) { boolean result = false; String value = removeThreadLockValue(key); if (value != null) { RedisSerializer keySerializer = redisTemplate.getKeySerializer(); RedisSerializer valueSerializer = redisTemplate.getValueSerializer(); byte[] keys = keySerializer.serialize(key); byte[] values = valueSerializer.serialize(value); Long t = redisTemplate.execute((RedisConnection connection) -> connection.eval(LUA_UN_LOCK.getBytes(), ReturnType.INTEGER, 1, keys, values) ); result = LUA_1L.equals(t); } return result; } private void setThreadLockValue(String key, String value) { Map<String, String> map = THREAD_LOCAL_LOCK.get(); if (map == null) { map = new HashMap<>(); } map.put(key, value); THREAD_LOCAL_LOCK.set(map); } private String getThreadLockValue(String key) { Map<String, String> map = THREAD_LOCAL_LOCK.get(); if (map != null) { return map.get(key); } return null; } private String removeThreadLockValue(String key) { Map<String, String> map = THREAD_LOCAL_LOCK.get(); if (map != null) { return map.remove(key); } return null; } }
redisUtils
最新推荐文章于 2024-09-30 11:23:38 发布