spring boot基于redis的LUA脚本 实现分布式锁【都是基于redis单点下】
一.spring boot 1.5.X 基于redis 的 lua脚本实现分布式锁
1.pom.xml
<!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2.RedisLock 工具类 (注入spring)
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Component; import java.util.Collections; /** * spring boot 1.5.X * 使用redis 的 lua脚本 基于单点实现分布式锁 * * lua脚本作为原子性操作,保证加锁和设置超时时间 为原子性操作 * @author sxd * @date 2019/5/27 10:52 */ @Component public class RedisLock { @Autowired RedisTemplate redisTemplate; private static final Long SUCCESS = 1L; /** * 获取锁 * * @param lockKey redis的key * @param value redis的value要求是随机串,防止释放其他请求的锁 * @param expireTime redis的key 的过期时间 防止死锁,导致其他请求无法正常执行业务 * @return */ public boolean lock(String lockKey, String value, int expireTime) { 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"; RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class); //对非string类型的序列化 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value, String.valueOf(expireTime)); return SUCCESS.equals(result); } /** * 释放锁 * * @param lockKey redis的key * @param value redis的value 只有value比对一致,才能确定是本请求 加的锁 才能正常释放 * @return */ public boolean unlock(String lockKey, String value) { 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); try { Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value); if (SUCCESS.equals(result)) { return true; } } catch (Exception e) { e.printStackTrace(); } return false; } }
3.controller使用
/** * 使用分布式锁 逻辑 * 1.准备好 key value expireTime * value要求是随机字符串 * expireTime 是根据业务 衡量决定的 锁过期时间 * * 2.获取锁 * 成功获取,则执行业务,执行完成,释放锁 * 失败获取,则重试获取,注意获取锁的时间间隔,直到获取成功,执行业务,最后释放锁 * * 注意: * 对于redis加锁的业务,尽量用在耗时短的业务上。 * */ @RequestMapping("/test") public void test(){ boolean flag = false; //标识 是否正常获取锁 String uuid = UUID.randomUUID().toString(); //redis的value 是一串随机数 flag = lock.lock("mykey1",uuid,5); if (flag){ business(uuid); }else { //如果未正常获取锁 可以通过重试 直到获取锁成功 while (!flag){ try { //重试 时间间隔 减少与redis交互次数 Thread.sleep(3000); System.out.println("重试"); flag = lock.lock("mykey1",uuid,5); if (flag){ business(uuid); }else { continue; } } catch (InterruptedException e) { e.printStackTrace(); } } } } public void business(String uuid){ try { System.out.println("加锁成功,执行业务"); Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }finally { //业务执行完成 正常释放锁 lock.unlock("mykey1",uuid); } }
二.spring boot 2.x 基于redis 的LUA脚本 实现分布式锁
1.