使用redis做分布式锁

一、为什么需要使用分布式锁呢?

       在分布式系统中,或者传统集群部署项目中,在并发需要保证资源安全使用的情况。jvm中的锁没有办法真正保证资源安全的。至于为什么呢?简单理解为jvm锁只能在各自的容器中实现锁机制,在自身的jvm容器之前也就没有办法控制了。

二、分布式锁实现需要注意些什么东西呢?

       这个是参考的一篇博客,里面说到的比较完善,建议大家看看,与此同时也十分感谢该博客的作者。
https://www.jianshu.com/p/a1ebab8ce78a
我个人觉得里面讲得不错。自己也在自己的理解上居于redis实现了一个通用的分布式锁组件(工具类)

三、个人实现的分布式锁主要代码如下,为了保证原子性,我使用了lua脚本

/**
 * @Author: ChenHuaMing
 * @Date: 2020/10/9 14:53
 * @Description: 分布式锁(redis实现)
 */
public class RedisLockServiceImpl implements ILockService {
    private final Logger logger= LoggerFactory.getLogger(RedisLockServiceImpl.class);
    private StringRedisTemplate stringRedisTemplate;
    private RedisScript<Boolean> defaultRedisScript;
    private String timeOut;
    private ObjectMapper objectMapper=new ObjectMapper();
    private final String PREFIX="hadoop:lock:";

    public RedisLockServiceImpl(StringRedisTemplate stringRedisTemplate, RedisScript<Boolean> defaultRedisScript, String timeOut) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.defaultRedisScript = defaultRedisScript;
        this.timeOut = timeOut;
    }

    @Override
    public boolean getLock(String key,String expireTime) {
        if(StringUtils.isEmpty(expireTime)){
            expireTime=getTimeOut();
        }
        long lockTime = System.currentTimeMillis();
        LockValue redisLockValue = new LockValue(key,getThreadId(), lockTime, (lockTime + Long.valueOf(expireTime)));
        try {
            String value = objectMapper.writeValueAsString(redisLockValue);
            List<String> keys = Arrays.asList(PREFIX+key, value);
            Boolean execute = stringRedisTemplate.execute(defaultRedisScript, keys, expireTime);
            return execute == null?false:execute;
        } catch (JsonProcessingException e) {
            logger.error("transport lockValue error in lock",e);
            return false;
        }
    }

    @Override
    public boolean unlock(String key) {
        String lockValueStr = stringRedisTemplate.opsForValue().get(PREFIX + key);
        try {
            if(!StringUtils.isEmpty(lockValueStr)){
                LockValue lockValue = objectMapper.readValue(lockValueStr, LockValue.class);
                if(!getThreadId().equals(lockValue.getThreadId())){
                    throw new RuntimeException("线程id不一致,不允许解锁");
                }
                if(!lockValue.getBussId().equals(key)){
                    throw new RuntimeException("业务id不一致,不允许解锁");
                }
                return stringRedisTemplate.delete(PREFIX+key);
            }else{
                return false;
            }
        } catch (JsonProcessingException e) {
            logger.error("transport lockValue error in unlock",e);
            return false;
        }
    }

    private String getThreadId(){
        return new StringBuilder()
                .append(IpUtils.getLocalIpAddress())
                .append("-")
                .append(Thread.currentThread().getId()).toString();
    }

    @Override
    public String getTimeOut() {
        return timeOut;
    }
}
--- 获取key
local key = KEYS[1]
--- 获取value
local val = KEYS[2]
--- 获取一个参数
local expire = ARGV[1]
--- 如果redis找不到这个key就去插入
if redis.call("get", key) == false then
    --- 如果插入成功,就去设置过期值
    if redis.call("set", key, val) then
        if tonumber(expire) > 0 then
            --- 设置过期时间
            redis.call("expire", key, expire)
        end
        return true
    end
    return false
else
    return false
end

对应源码我放在了码云上,有需要的朋友可以自行下载
https://gitee.com/MingAndTao/ljm-simple-base/tree/master/ljm/ljm-cache/src/main/java/com/ljm/lock
我是按组件方式实现的,觉得可以的朋友,可以直接拷贝组件是使用

四、redis方式实现的分布式锁组件使用方法

使用方法请参考这个wiki:https://gitee.com/MingAndTao/ljm-simple-base/wikis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E4%BD%BF%E7%94%A8?sort_id=2969890

博客写得比较简陋,请多多包涵

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值