工作中用到的分布式锁

120 阅读2分钟
import cn.hutool.core.util.IdUtil;
import com.jovision.vse.console.core.constant.RedisConstant;
import com.jovision.vse.console.core.exception.BusinessErrorEnum;
import com.jovision.vse.console.core.exception.MixException;
import com.jovision.vse.console.core.model.LockContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.error.Mark;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Description 获取锁, 释放锁
 * @Author FL
 * @Date 11:29 2021/10/18
 * @Param
 **/
@Service
@Slf4j
public class DeviceLock {

    @Autowired
    StringRedisTemplate redisTemplate;

    /**
     * @Description 获取锁
     * @Author FL
     * @Date 11:35 2021/10/18
     * @Param [tenantId]
     **/
    public LockContext getLock(String tenantId) {
        return getLock(tenantId, null);
    }

    /**
     * 获取锁
     *
     * @param tenantId 企业id
     * @param type     锁的类型
     * @return
     */
    public LockContext getLock(String tenantId, String type) {

        /**
         * 一个租户一个锁,锁的自动释放时间为300ms
         * 获取锁时正在被占用,120ms为间隔,最多重试三次,三次获取,均为被占用,提示当前系统繁忙,请稍后重试
         **/
        // 获取锁
        String simpleUUID = IdUtil.simpleUUID();
        String lockKey = RedisConstant.LOCK_KEY + "getRemainingChannelNumber:" + tenantId;
        if (StringUtils.isNotBlank(type)) {
            lockKey += ":" + type;
        }
        Boolean lockState = redisTemplate.opsForValue().setIfAbsent(lockKey
                , simpleUUID, 300, TimeUnit.MILLISECONDS);

        log.info("获取锁: 状态{} 值:{}", lockState, simpleUUID);

        int maxRetryCount = 4;// 最大重试次数3次,一共4次
        int index = 1;//抢占次数

        while (!lockState) {
            try {
                Thread.sleep(120);
                log.info("抢占失败线程: {} 租户:{}", Thread.currentThread().getId(), tenantId);
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.info(e.getMessage());
            }
            index++;
            lockState = redisTemplate.opsForValue().setIfAbsent(lockKey
                    , simpleUUID, 300, TimeUnit.MILLISECONDS);
            if (!lockState && index >= maxRetryCount) {
                throw new MixException(BusinessErrorEnum.THE_SYSTEM_WAS_BUSY_EARLIER);
            }
        }

        log.info("抢占成功线程: {} 租户:{}", Thread.currentThread().getId(), tenantId);

        return LockContext.builder().key(lockKey).value(simpleUUID).build();
    }

    /**
     * @Description 释放锁
     * @Author FL
     * @Date 11:35 2021/10/18
     * @Param [key]
     **/
    public void releaseLock(LockContext lockContext) {
        if(null != lockContext){
            String value = lockContext.getValue();
            String key = lockContext.getKey();
            if (value.equals(redisTemplate.opsForValue().get(key))) {
                log.info("释放锁: 值:{}", value);
                redisTemplate.delete(key);
            }
        }
    }


    /**
     * ===========废弃=========
     * 获取锁
     * 通道布控绑定,标记为该通道需要创建分组
     * 任务执行加分布式锁,只创建一次分组
     * 抢占成功,创建分组移除标记
     * 抢占失败,重试是否获取到 创建分组 标记,重试三次;
     * 标记获取成功且超过限制,默认任务失败; 标记获取失败且未超过限制,继续执行;
     */
    public LockContext getFaceLock(String channelId) {
        String lock_key_create = RedisConstant.LOCK_KEY + RedisConstant.FACE_CHANNEL + channelId;// 新增分组通道锁
        String lock_key_mark = RedisConstant.LOCK_KEY + RedisConstant.FACE_GROUP + channelId;// 创建分组标记

        String simpleUUID = IdUtil.simpleUUID();
        // 获取锁
        Boolean lockState = redisTemplate.opsForValue().setIfAbsent(lock_key_create,
                simpleUUID, 500, TimeUnit.MILLISECONDS);
        log.info("获取锁: 状态{} 值:{}", lockState, simpleUUID);

        int maxRetryCount = 4;// 最大重试次数3次
        int index = 0;//重试次数

        while (!lockState) {
            try {
                Thread.sleep(220);
                log.info("抢占失败线程: {} 通道:{}", Thread.currentThread().getId(), channelId);
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.info(e.getMessage());
            }
            index++;
            Boolean markTry = redisTemplate.hasKey(lock_key_mark);
            if (!markTry && index < maxRetryCount) {
                return null;
            }
            if (markTry && index >= maxRetryCount) {
                throw new MixException(BusinessErrorEnum.BASE_TOP_SERVER.getCode(), "任务执行失败");
            }
        }
        log.info("抢占成功线程: {} 通道:{}", Thread.currentThread().getId(), channelId);
        return LockContext.builder().key(lock_key_create).value(simpleUUID).build();
    }


    /**
     * @Description 获取分布式锁
     * @Author FL
     * @Date 11:33 2022/1/12
     * @Param [channelId] 锁的key重要组成
     **/
    public LockContext getDistributeLock(String channelId) {
        String lock_key_create = RedisConstant.LOCK_KEY + RedisConstant.FACE_CHANNEL + channelId;// 新增分组通道锁
        String simpleUUID = IdUtil.simpleUUID();
        // 获取锁
        Boolean lockState = redisTemplate.opsForValue().setIfAbsent(lock_key_create,
                simpleUUID, 3000, TimeUnit.MILLISECONDS);
        log.info("获取锁: 状态{} 值:{}", lockState, simpleUUID);
        if (!lockState) {
            log.info("抢占失败线程: {} 通道:{}", Thread.currentThread().getId(), channelId);
            return null;
        }
        log.info("抢占成功线程: {} 通道:{}", Thread.currentThread().getId(), channelId);
        return LockContext.builder().key(lock_key_create).value(simpleUUID).build();
    }

    /**
     * @Description: 获取accesstokenLock
     * @Param: [ak]
     * @return: boolean
     * @Author: zhaowenchao
     * @Date: 2022/1/11
     */
    public LockContext getAccessTokenLock(String ak){
        // 获取锁
        String simpleUUID = IdUtil.simpleUUID();
        String lockKey = RedisConstant.LOCK_KEY + "getAccessToken:" + ak ;
        Boolean lockState = redisTemplate.opsForValue().setIfAbsent(lockKey
                , simpleUUID, 500, TimeUnit.MILLISECONDS);
        log.info("getAccessTokenLock 获取锁: 状态{} 值:{}", lockState, simpleUUID);

        if(lockState){
            return LockContext.builder().key(lockKey).value(simpleUUID).build();
        }else{
            return null;
        }
    }
}

\