1. 简介
在分布式系统中,一个服务可以启动多个副本,当多个副本同时做一件事情时就会出现并行的问题,如果在新增数据时出现并行时往往会造成数据流水号的重复问题,为解决这个问题出现了很多中方法,如分布式锁、消息队列等,其中分布式锁又根据中间件分为redis分布式锁、zookeeper分布式锁、db分布式锁等版本,今天我介绍一下自己实现的redis分布式锁。
大概流程:
- Servcie去抢占锁
- 抢占成功后设置锁过期时间为2S(防止死锁),返回抢占成功状态
- Servcie执行业务操作
- Service业务操作执行完成通知Redis释放锁
2. 实现
1. 封装分布式锁
@Component
public class RedisLock implements Lock {
private static String lockKey = "YC_RedisLockKey";
private static int LockExpire = 1000;
private ThreadLocal<String> localValue = new ThreadLocal<>();
@Autowired
private RedisTemplate redisTemplate;
public void setKey(String category) {
lockKey = "YC_RedisLockKey_" + category;
}
@Override
public void lock() {
if (tryLock()) {
return;
}
try {
Thread.sleep(10);
} catch (Exception ex) {
ex.printStackTrace();
}
lock();
}
@Override
public boolean tryLock() {
String uuid = UUID.randomUUID().toString();
if (redisTemplate.opsForValue().setIfAbsent(lockKey,uuid,LockExpire,TimeUnit.MILLISECONDS)) {
// redisTemplate.expire(lockKey, LockExpire, TimeUnit.SECONDS);
localValue.set(uuid);
return true;
}
return false;
}
@Override
public void unlock() {
if (localValue.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public Condition newCondition() {
return null;
}
}
2. 调用分布式锁
/**
* 获取下一个流水号
*
* @param category 流水号分类
* @return Ex.:PT00000012
* @throws Exception
*/
public String getNext(String category) throws Exception {
return category + String.format("%08d", getAndIncrement(category));
}
private Long getAndIncrement(String category) throws Exception {
String dataKey = "YC_RedisDataKey_" + category;
Long num;
redisLock.setKey(category);
redisLock.lock();
Object obj = redisTemplate.opsForValue().get(dataKey);
if (obj == null) {
redisTemplate.opsForValue().set(dataKey, 1L);
num = 1L;
} else {
num = Long.valueOf(obj.toString());
redisTemplate.opsForValue().set(dataKey, ++num);
}
redisLock.unlock();
return num;
}