RedisTemplete 实现分布式锁

RedisTemplete 实现分布式锁

redis 分布式锁

项目中设置了定时任务,而项目属于微服务架构,后续会采用多点部署。考虑到多个服务节点只需运行一次的特点,所以需要采用分布式锁在同一时间只运行一次代码。中间也踩过不少坑,不断的修复。
在使用的过程中需要注意两点:
1、redis的配置需要设置超时等待时间,否则会在获取不到redis资源的时候出现阻塞等待,导致方法无法退出。
2、由于在分布式锁中使用了redis的事务管理,而在redisTemplete中是不会主动释放资源的,这也会导致redis连接数不够,从而出现第一个问题。所以我们需要在释放锁的时候同时也释放redis连接。
3、在任务过程中。我们需要注意的是守护续约线程的及时关闭。在一些操作结束后我们需要在finally 中释放以及关闭。

RedisLock

package com.xxx.xxx.xxx.xxx.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author 豪猪不挡道
 * @version 1.0
 * @desc redis分布式锁事务实现
 * @date 2021/1/20 20:04
 */
@Slf4j
@Component
public class RedisLock {


    private static ThreadLocal<Thread> THREA_LOCAL = new ThreadLocal<>();
    private final String lock_prefix = "lock_prefix:";
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * @param lock
     * @param timeoutSeconds
     * @return
     * @throws InterruptedException
     */
    public String lock(String lock, long timeoutSeconds) throws InterruptedException {
        log.info(Thread.currentThread().getId() + " get lock begin...");
        try {
            String identification = UUID.randomUUID().toString();
            String lockName = String.format("%s%s", lock_prefix, lock);

            Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockName, identification, timeoutSeconds, TimeUnit.SECONDS);
            if (flag) {
                // 续签
                Thread renewal = new Thread(new RenewalLock(lock, identification, timeoutSeconds));
                renewal.setDaemon(true);
                THREA_LOCAL.set(renewal);
                renewal.start();
                return identification;
            }
        } finally {
            log.info(Thread.currentThread().getId() + " get lock end...");

        }

        return "";
    }

    /**
     * @param lock
     * @param identification
     */
    public void release(String lock, String identification) {
        try {
            redisTemplate.setEnableTransactionSupport(true);
            String lockName = String.format("%s%s", lock_prefix, lock);
            redisTemplate.watch(lockName);
            String identificationDB = redisTemplate.opsForValue().get(lockName);
            if (identificationDB != null && identification != null && identification.equals(identificationDB)) {
                redisTemplate.multi();
                redisTemplate.delete(lockName);
                redisTemplate.exec();
            }
            redisTemplate.unwatch();
        } finally {
            RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
            THREA_LOCAL.get().interrupt();
            THREA_LOCAL.remove();
        }
    }

    /**
     * 续签lock
     */
    private class RenewalLock implements Runnable {

        private String lockName;
        private String identification;
        private Long timeout;

        public RenewalLock(String lock, String identification, Long timeout) {
            this.lockName = lock_prefix + lock;
            this.identification = identification;
            this.timeout = timeout;
        }

        @Override
        public void run() {
            int i = 0;
            while (true) {
                try {
                    if (Thread.currentThread().isInterrupted()) {
                        log.info("RenewalLock end.");
                        return;
                    }
                    TimeUnit.SECONDS.sleep(timeout / 2);
                    redisTemplate.setEnableTransactionSupport(true);
                    redisTemplate.watch(lockName);
                    String identificationDB = redisTemplate.opsForValue().get(lockName);
                    if (identificationDB != null && identification != null && identificationDB.equals(identification)) {
                        redisTemplate.multi();
                        redisTemplate.expire(lockName, timeout, TimeUnit.SECONDS);
                        redisTemplate.exec();
                        log.debug(Thread.currentThread().getId() + ": reset expire time ok, " + ++i);
                    }
                    redisTemplate.unwatch();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error("InterruptedException renewalLock end.");
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                    log.error("Exception renewalLock end.");
                    log.info(e.getMessage(), e);
                } finally {
                    RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
                }
            }
        }
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值