针对RedisTemplate分布式锁实现WatchDog

2023-04-16 13:23:10 浏览数 (2)

在此之前,去看了下Redission的实现原理,不过在开发中,原本的代码使用RedistTemplate实现的,也不太想换,所以我想了下,不如自己实现要给WatchDog。

我的想法是,在用户加上锁的时候开启个定时任务线程,并且在定时任务中,判断原线程isAlive状态进行“续命”。

下面是代码(在这里面为了方便,未使用的是HuTool.CornUtil来实现动态定时任务):

代码语言:java复制
/**
 * Title
 *
 * @ClassName: LockUtil
 * @Description:锁工具类,通过内部枚举类实现单例,防止反射攻击
 * @author: Karos
 * @date: 2023/1/4 0:17
 * @Blog: https://www.wzl1.top/
 */

package cn.katool.lock;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.cron.task.Task;
import cn.katool.Config.LockConfig;
import cn.katool.Exception.ErrorCode;
import cn.katool.Exception.KaToolException;
import cn.katool.other.MethodIntefaceUtil;
import com.qiniu.util.StringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Component
@Scope("prototype")
@Slf4j
public class LockUtil {
        @Resource
        RedisTemplate redisTemplate;
        private LockUtil(){

        }
        private static boolean isOpenCorn=false;

        /**
         * 带看门狗机制上锁
         * @param lockObj
         * @return
         */
        public boolean DistributedLock(Object lockObj){
                try {
                        return DistributedLock(lockObj,null,null);
                } catch (KaToolException e) {
                        throw new RuntimeException(e);
                }
        }
        @Resource
        LockConfig lockConfig;
        //加锁

        /**
         * 无看门狗机制上锁
         * @param obj
         * @param exptime
         * @param timeUnit
         * @return
         * @throws KaToolException
         */
        public boolean DistributedLock(Object obj,Long exptime,TimeUnit timeUnit) throws KaToolException {
                if (ObjectUtil.isEmpty(obj)){
                        throw new KaToolException(ErrorCode.PARAMS_ERROR," Lock=> 传入obj为空");
                }
                Boolean isDelay=false;
                if (ObjectUtil.isAllEmpty(exptime,timeUnit)){
                        isDelay=true;
                }
                if(ObjectUtil.isEmpty(exptime)){
                        exptime= lockConfig.getInternalLockLeaseTime();;
                }
                if (ObjectUtils.isEmpty(timeUnit)){
                        timeUnit=lockConfig.getTimeUnit();
                }
                //线程被锁住了,就一直等待
                DistributedAssert(obj);
                Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("Lock:" obj.toString(), "1", exptime, timeUnit);
                log.info("katool=> LockUntil => DistributedLock:{} value:{} extime:{} timeUnit:{}",obj.toString(), "1", exptime, timeUnit);
                //实现看门狗
                if (isDelay){
                        if (LockUtil.isOpenCorn==false){
                                //如果同一个项目之前打开过,那么先关闭,避免重复启动
                                CronUtil.stop();
                                //支持秒级别定时任务
                                CronUtil.setMatchSecond(true);
                                //定时服务启动
                                CronUtil.start();
                                LockUtil.isOpenCorn=true;
                        }
                        Thread thread = Thread.currentThread();
                        TimeUnit finalTimeUnit = timeUnit;
                        Long finalExptime = exptime;
                        class TempClass{
                                public String scheduleId;
                        }
                        final TempClass tempClass = new TempClass();
                        tempClass.scheduleId=CronUtil.schedule("0/30 * * * * ?", new Task() {
                                @SneakyThrows
                                @Override
                                public void execute() {
                                        boolean alive = thread.isAlive();
                                        if (alive) {
                                                delayDistributedLock(obj, finalExptime>=3?(finalExptime / 3):finalExptime, finalTimeUnit);
                                                return;
                                        } else {
                                                if (tempClass.scheduleId==null||"".equals(tempClass.scheduleId)){
                                                        return;
                                                }
                                                CronUtil.remove(tempClass.scheduleId);
                                                DistributedUnLock(obj);
                                                return;
                                        }
                                }
                        });
                }
                return BooleanUtil.isTrue(aBoolean);
        }

        //检锁
        public void DistributedAssert(Object obj) throws KaToolException {
                if (ObjectUtils.isEmpty(obj)){
                        throw new KaToolException(ErrorCode.PARAMS_ERROR," Lock=> 传入obj为空");
                }
                while(true){
                        Object o = redisTemplate.opsForValue().get("Lock:"   obj.toString());
                        if (ObjectUtils.isEmpty(o))return;
                }
        }

        //延期
        public boolean delayDistributedLock(Object obj,Long exptime,TimeUnit timeUnit) throws KaToolException {
                if (ObjectUtils.isEmpty(obj)){
                        throw new KaToolException(ErrorCode.PARAMS_ERROR," Lock=> 传入obj为空");
                }
                Boolean aBoolean = redisTemplate.opsForValue().setIfPresent("Lock:" obj.toString(), "1", exptime, timeUnit);
                log.info("katool=> LockUntil => delayDistributedLock:{} value:{} extime:{} timeUnit:{}",obj.toString(), "1", exptime, timeUnit);
                return BooleanUtil.isTrue(aBoolean);
        }
        //释放锁
        public boolean DistributedUnLock(Object obj) throws KaToolException {
                if (ObjectUtils.isEmpty(obj)){
                        throw new KaToolException(ErrorCode.PARAMS_ERROR," Lock=> 传入obj为空");
                }
                Boolean aBoolean = redisTemplate.delete("Lock:"   obj.toString());
                log.info("katool=> LockUntil => unDistributedLock:{} isdelete:{} ",obj.toString(),true);
                return BooleanUtil.isTrue(aBoolean);
        }



        //利用枚举类实现单例模式,枚举类属性为静态的
        private enum SingletonFactory{
                Singleton;
                LockUtil lockUtil;
                private SingletonFactory(){
                        lockUtil=new LockUtil();
                }
                public LockUtil getInstance(){
                        return lockUtil;
                }
        }
        @Bean("LockUtil")
        public static LockUtil getInstance(){
                return SingletonFactory.Singleton.lockUtil;
        }
}

0 人点赞