配置文件:bootstrap-dev.yml
redis: database: 0 host: iac-redis-dev-tech.ikunchi.com port: 30038 password: timeout: 60000ms jedis: pool: # 連接池最大阻塞等待時間(使用負值表示沒有限制) max-wait: -1ms # 連接池最大連接數(使用負值表示沒有限制) max-active: -1
RedisConfig.java
@Configuration public class RedisConfig { @Bean @SuppressWarnings("all") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
CacheService.java
@Service public class CacheService { private static Logger logger = LoggerFactory.getLogger(CacheService.class); private static final Long SUCCESS = 1L; @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 指定緩存失效時間 * * @param key 鍵 * @param time 時間(秒) * @return */ public boolean expire(String key, long time) { try { if (time > 0) { redisTemplate.expire(key, time, TimeUnit.SECONDS); } return true; } catch (Exception e) { logger.error(e.getMessage(), e); return false; } } /** * 根據key 獲取過期時間 * * @param key 鍵 不能為null * @return 時間(秒) 返回0代表為永久有效 */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 判斷key是否存在 * * @param key 鍵 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { logger.error(e.getMessage(), e); return false; } } /** * 刪除緩存 * * @param key 可以傳一個值 或多個 */ @SuppressWarnings("unchecked") public void del(String... key) { if (key != null && key.length > 0) { if (key.length == 1) { redisTemplate.delete(key[0]); } else { redisTemplate.delete(CollectionUtils.arrayToList(key)); } } } /** * 普通緩存獲取 * * @param key 鍵 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * 普通緩存放入 * * @param key 鍵 * @param value 值 * @return true成功 false失敗 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { logger.error(e.getMessage(), e); return false; } } /** * 普通緩存放入並設置時間 * * @param key 鍵 * @param value 值 * @param time 時間(秒) time要大於0 如果time小於等於0 將設置無限期 * @return true成功 false 失敗 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { set(key, value); } return true; } catch (Exception e) { logger.error(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); } /** * 遞減 * * @param key 鍵 * @param delta 要減少幾(小於0) * @return 147 */ public long decr(String key, long delta) { if (delta < 0) { throw new RuntimeException("遞減因子必須大於0"); } return redisTemplate.opsForValue().increment(key, -delta); } /** * 獲取分布式鎖 * @param lockKey 鎖 * @param requestId 請求標識 * @param expireTime 單位秒 * @param waitTimeout 單位毫秒 * @return 是否獲取成功 */ public boolean tryLock(String lockKey, String requestId, int expireTime,long waitTimeout) { long nanoTime = System.nanoTime(); // 當前時間 try{ String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end"; logger.info("開始獲取分布式鎖-key[{}]",lockKey); int count = 0; do{ RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class); logger.debug("嘗試獲取分布式鎖-key[{}]requestId[{}]count[{}]",lockKey,requestId,count); Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),requestId,expireTime); if(SUCCESS.equals(result)) { logger.debug("嘗試獲取分布式鎖-key[{}]成功",lockKey); return true; } Thread.sleep(500L);//休眠500毫秒 count++; }while ((System.nanoTime() - nanoTime) < TimeUnit.MILLISECONDS.toNanos(waitTimeout)); }catch(Exception e){ logger.error("嘗試獲取分布式鎖-key[{}]異常",lockKey); logger.error(e.getMessage(),e); } return false; } /** * 釋放鎖 * @param lockKey 鎖 * @param requestId 請求標識 * @return 是否釋放成功 */ public boolean releaseLock(String lockKey, String requestId) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class); Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId); if (SUCCESS.equals(result)) { return true; } return false; } }
加鎖另一種方式,試過可用,上面未驗證
/*** * 加鎖 * @param key * @param value 當前時間+超時時間 * @return 鎖住返回true */ public boolean lock(String key,String value){ if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//setNX 返回boolean return true; } //如果鎖超時 *** String currentValue = stringRedisTemplate.opsForValue().get(key); if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue)<System.currentTimeMillis()){ //獲取上一個鎖的時間 String oldvalue = stringRedisTemplate.opsForValue().getAndSet(key,value); if(!StringUtils.isEmpty(oldvalue)&&oldvalue.equals(currentValue)){ return true; } } return false; } /*** * 解鎖 * @param key * @param value * @return */ public void unlock(String key,String value){ try { String currentValue = stringRedisTemplate.opsForValue().get(key); if(!StringUtils.isEmpty(currentValue)&¤tValue.equals(value)){ stringRedisTemplate.opsForValue().getOperations().delete(key); } } catch (Exception e) { log.error("解鎖異常"); } }