1. 简介

在分布式系统中,一个服务可以启动多个副本,当多个副本同时做一件事情时就会出现并行的问题,如果在新增数据时出现并行时往往会造成数据流水号的重复问题,为解决这个问题出现了很多中方法,如分布式锁、消息队列等,其中分布式锁又根据中间件分为redis分布式锁、zookeeper分布式锁、db分布式锁等版本,今天我介绍一下自己实现的redis分布式锁。

分布式锁实现流水号的自增长-LMLPHP

大概流程:

  1. Servcie去抢占锁
  2. 抢占成功后设置锁过期时间为2S(防止死锁),返回抢占成功状态
  3. Servcie执行业务操作
  4. 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;
    }
03-14 05:59