简易的Redis分布式事务锁

梦想与她 提交于 2020-08-10 18:37:30

0.背景

  在物联网微服务时代,面对源源不断的数据,难免会遇到需要一把分布式事务锁的情况,今天,我们学习一下Redis分布式事务锁。

1.用于上锁的类

RedisLock.java

public class RedisLock {

    private static final Logger log = LoggerFactory.getLogger(RedisLock.class);

    @Resource(name = "deviceStringRedisTemplate")
    private StringRedisTemplate redisTemplate;

    /**
     * redis键过期时间,毫秒为单位
     */
    private static final long EXPIRE = 5 * 1000L;

    /**
     * 尝试获取锁的超时时间,不能一直尝试获取锁,空耗资源。
     */
    private static final long TIMEOUT = 10 * 1000L;

    public boolean lock(String key, String value) {
        log.info("尝试获取锁 kye:{},value:{}", key, value);
        //请求锁时间
        long requestTime = System.currentTimeMillis();
        while (true) {
            //等待锁时间
            long watiTime = System.currentTimeMillis() - requestTime;
            //如果等待锁时间超过10s,加锁失败
            if (watiTime > TIMEOUT) {
                log.info("等待锁超时 kye:{},value:{}", key, value);
                return false;
            }

            if (redisTemplate.opsForValue().setIfAbsent(key, String.valueOf(System.currentTimeMillis()))) {
                //获取锁成功
                log.info("获取锁成功 kye:{},value:{}", key, value);
                //设置超时时间,防止解锁失败,导致死锁
                redisTemplate.expire(key, EXPIRE, TimeUnit.MILLISECONDS);
                return true;
            }

            String valueTime = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(valueTime) && System.currentTimeMillis() - Long.parseLong(valueTime) > EXPIRE) {
                //加锁时间超过过期时间,删除key,防止死锁
                log.info("锁超时, key:{}, value:{}", key, value);
                try {
                    redisTemplate.opsForValue().getOperations().delete(key);
                } catch (Exception e) {
                    log.info("删除锁异常 key:{}, value:{}", key, value);
                    e.printStackTrace();
                }
                return false;
            }

            // 获取锁失败,等待20毫秒继续请求
            try {
                log.info("等待20 nanoSeconds key:{},value:{}", key, value);
                TimeUnit.NANOSECONDS.sleep(20);
            } catch (InterruptedException e) {
                log.info("等待20 nanoSeconds 异常 key:{},value:{}", key, value);
                e.printStackTrace();
            }
        }
    }


    /**
     * 解锁
     *
     * @param key
     * @param value
     */
    public void unlock(String key, String value) {
        try {
            redisTemplate.opsForValue().getOperations().delete(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.应用举例

  我们以得到锁之后的第一个线程才处理业务为例,给出一个例子:


String redisKey = RedisPrefix.MY_BIZ + name;
            try {
                boolean lockResult = redisLock.lock(redisKey, name);
                if (lockResult) {
                    userInfo = userService.findUser(name);
                    if (userInfo == null) {
                        // 抢到锁,且为第一个到来的线程,开始处理业务。
                        userService.createUserInfo(name);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("redis lock error:", e);
            } finally {
                redisLock.unlock(redisKey, name);
            }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!