分布式锁的实现方式常用的就是redis和zookeeper了,今天介绍一下redis实现分布式锁
1.基于spring-data-redis中的redisTemplate
直接上代码
@Slf4j public class RedisLockUtils { /** * tryLock 默认过期时间秒, 高并发场景可以使用锁续命(定时任务每隔指定时间重新加锁设置时间) * * @param redisTemplate * @param key * @param expireTime * @return */ public static boolean tryLock(RedisTemplate redisTemplate, String key, String clientId, Long expireTime) { return tryLock(redisTemplate, key, clientId, expireTime, TimeUnit.SECONDS); } /** * * @param redisTemplate * @param key * @param clientId UUID.randomUUID().toString() 防止高并发情况下相同的key被不同锁删除 * @param expireTime * @param timeUnit * @return */ public static boolean tryLock(RedisTemplate redisTemplate, String key, String clientId, Long expireTime, TimeUnit timeUnit) { Boolean success = redisTemplate.opsForValue().setIfAbsent(key, clientId, expireTime, timeUnit); log.info("tryLock key = {}, value = {}, expireTime = {}", key, clientId, expireTime); return success != null && success; } /** * unLock * * @param redisTemplate * @param key * @return */ public static boolean unLock(RedisTemplate redisTemplate, String key, String clientId) { Boolean success = false; if (clientId.equals(redisTemplate.opsForValue().get(key))) { success = redisTemplate.delete(key); } log.info("unLock key = {}", key); return success; } }
上述代码在并发量不大的情况下没什么大问题,但是高并发的情况下和某些资源耗时的情况下要考虑使用定时任务或者定时器进行锁续命
2.使用开源框架redisson,基于springboot redisson框架
DistributeLock
public interface DistributeLock { RLock lock(String lockKey); RLock lock(String lockKey, int timeout); RLock lock(String lockKey, TimeUnit unit, int timeout); boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime); void unlock(String lockKey); void unlock(RLock lock); }
RedisDistributeLock
@Component public class RedisDistributeLock implements DistributeLock { @Autowired RedissonClient redissonClient; @Override public RLock lock(String lockKey) { RLock rLock = redissonClient.getLock(lockKey); rLock.lock(); return rLock; } @Override public RLock lock(String lockKey, int timeout) { RLock rLock = redissonClient.getLock(lockKey); rLock.lock(timeout, TimeUnit.SECONDS); return rLock; } @Override public RLock lock(String lockKey, TimeUnit unit, int timeout) { RLock rLock = redissonClient.getLock(lockKey); rLock.lock(timeout, unit); return rLock; } @Override public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { return false; } } @Override public void unlock(String lockKey) { RLock rLock = redissonClient.getLock(lockKey); rLock.unlock(); } @Override public void unlock(RLock lock) { lock.unlock(); } }
RedisLockUtils
public class RedisLockUtils { private static DistributeLock distributeLock = SpringContext.getBean("distributeLock", DistributeLock.class); /** * 加锁 * @param lockKey * @return */ public static RLock lock(String lockKey) { return distributeLock.lock(lockKey); } /** * 释放锁 * @param lockKey */ public static void unlock(String lockKey) { distributeLock.unlock(lockKey); } /** * 释放锁 * @param lock */ public static void unlock(RLock lock) { distributeLock.unlock(lock); } /** * 带超时的锁 * @param lockKey * @param timeout 超时时间 单位:秒 */ public static RLock lock(String lockKey, int timeout) { return distributeLock.lock(lockKey, timeout); } /** * 带超时的锁 * @param lockKey * @param unit 时间单位 * @param timeout 超时时间 */ public static RLock lock(String lockKey, int timeout, TimeUnit unit ) { return distributeLock.lock(lockKey, unit, timeout); } /** * 尝试获取锁 * @param lockKey * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放锁时间 * @return */ public static boolean tryLock(String lockKey, int waitTime, int leaseTime) { return distributeLock.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime); } /** * 尝试获取锁 * @param lockKey * @param unit 时间单位 * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放锁时间 * @return */ public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { return distributeLock.tryLock(lockKey, unit, waitTime, leaseTime); }
DistributeLockRest
@RestController @RequestMapping("/rest") public class DistributeLockRest { @Autowired Environment environment; private int num1 = 10; private int num2 = 10; Lock lock = new ReentrantLock(); @Autowired DistributeLock distributeLock; @GetMapping("/test") public String test() { return environment.getProperty("server.port"); } @RequestMapping("/distributeLock") public void distributeLock() { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i=0;i<10;i++) { executorService.submit(() -> { sub1(); System.out.println(num1); }); } num1=10; System.out.println("========"); } @RequestMapping("/localLock") public void lock() { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i=0;i<10;i++) { executorService.submit(() -> { sub2(); System.out.println(num2); }); } num2=10; System.out.println("______________"); } public void sub1() { distributeLock.lock("test"); num1--; distributeLock.unlock("test"); } public void sub2() { lock.lock(); num2--; lock.unlock(); } }
redisson框架基于redis实现的分布式锁大量的使用了lua脚本,锁续命使用的TimerTask实现
3.idea设置不同的端口启动
上面端口改了启动两次,然后在虚拟机配置一个nginx
配置负载均衡修改/usr/local/nginx/conf/nginx.conf
添加
启动nginx
在/usr/local/nginx/sbin ./nginx
4.启动jemeter
并发测试发现使用分布式锁是ok的,但是jvm的Lock锁进行减操作会减成负数