基于 redisTemplate
在分布式集群环境中的最佳实践,其实无论是单机还是集群,保证原子性都是第一位的,如果能同时保证性能和高可用,那么就是一个可靠的分布式锁解决方案。
主要思路是:设置锁时,使用 redisTemplate,因为其底层实际包含了 setnx 、expire 的功能,起到了原子操作的效果. 给 key 设置随机且唯一的值,并且只有在 key 不存在时才设置成功返回 True,并且设置 key 的过期时间(最好是毫秒级别)
完整代码:
定义一个接口 和实现类:
public interface ILock {/*** 获取锁* @param timeout 超时自动解锁* @return 获取到锁返回true 获取失败则返回false*/boolean tryLock(long timeout);/*** 释放锁*/void unlock();
}
public class RedisLock implements ILock {private String name;private StringRedisTemplate stringRedisTemplate;public RedisLock (String name,StringRedisTemplate redisTemplate){this.name = name;this.stringRedisTemplate = redisTemplate;}private static final String KEY_PREFIX = "lock:";private static final String ID_PREFIX = UUID.randomUUID().toString();@Overridepublic boolean tryLock(long timeoutSec) {String threadId = ID_PREFIX + Thread.currentThread().getId();Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX+name,threadId);Boolean successExpire = stringRedisTemplate.expire(KEY_PREFIX+name,timeoutSec, TimeUnit.SECONDS);return success && successExpire;}@Overridepublic void unlock() {//获取线程标识String threadId = ID_PREFIX + Thread.currentThread().getId();//获取锁里面的标识String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX+name);if(threadId.equals(id)){stringRedisTemplate.delete(KEY_PREFIX+name);}}
使用:
@Component
@Slf4j
public class TestStatisticTask {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Scheduled(cron = "${test.statistic.overview.cron:0 0/5 * * * ?}")public void run(){log.info("定时器统计信息启动...");RedisLock redisLock = new RedisLock("TestStatisticTask",stringRedisTemplate);if(!redisLock.tryLock(5*60)){log.info("定时器统计信息启动 未获取到锁");return;}try{//业务处理逻辑}catch (Exception ex){log.info("######定时器统计信息启动 异常:{}", ex.getMessage());log.error(ex.getMessage(), ex);}finally {redisLock.unlock();}}
}