使用redis和zookeeper实现分布式锁
博客专区 > htlr 的博客 > 博客详情
使用redis和zookeeper实现分布式锁
htlr 发表于4个月前
使用redis和zookeeper实现分布式锁
  • 发表于 4个月前
  • 阅读 5018
  • 收藏 338
  • 点赞 8
  • 评论 18
摘要: 使用redis和zookeeper实现分布式锁

1.分布式锁

分布式锁一般用在分布式系统或者多个应用中,用来控制同一任务是否执行或者任务的执行顺序。在项目中,部署了多个tomcat应用,在执行定时任务时就会遇到同一任务可能执行多次的情况,我们可以借助分布式锁,保证在同一时间只有一个tomcat应用执行了定时任务。

2.分布式锁的实现方式

  1. 使用redis的setnx()和expire()
  2. 使用redis的getset()
  3. 使用zookeeper的创建节点node
  4. 使用zookeeper的创建临时序列节点

3.使用redis的setnx()和expire()来实现分布式锁##

setnx(key,value) 如果key不存在,设置为当前key的值为value;如果key存在,直接返回。
expire()来设置超时时间

定义注解类:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Lockable{
    // redis缓存key
    String key();
    // redis缓存key中的数据
    String value() default "";
    // 过期时间(秒),默认为一分钟
    long expire() default 60;
}

定时任务增加注解@Lockable:

 @Lockable(key = "DistributedLock:dealExpireRecords")
 public void dealExpireRecords() {
 }

定义一个aop切面LockAspect,使用@Around处理所有注解为@Lockable的方法,通过连接点确认此注解是用在方法上,通过方法获取注解信息,使用setIfAbsent来判断是否获取分布式锁,如果没有获取分布式锁,直接返回;如果获取到分布式锁,通过expire设置过期时间,并调用指定方法。

@Component
@Slf4j
@Aspect
public class LockAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    @Around("@annotation(com.records.aop.Lockable)")
    public Object distributeLock(ProceedingJoinPoint pjp) {
		Object resultObject = null;

		//确认此注解是用在方法上
        Signature signature = pjp.getSignature();
        if (!(signature instanceof MethodSignature)) {
            log.error("Lockable is method annotation!");
            return resultObject;
        }

        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();

        //获取注解信息
        Lockable lockable = targetMethod.getAnnotation(Lockable.class);
        String key = lockable.key();
        String value = lockable.value();
        long expire = lockable.expire();

        // 分布式锁,如果没有此key,设置此值并返回true;如果有此key,则返回false
        boolean result = redisTemplate.boundValueOps(key).setIfAbsent(value);
        if (!result) {
            //其他程序已经获取分布式锁
            return resultObject;
        }

        //设置过期时间,默认一分钟
        redisTemplate.boundValueOps(key).expire(expire, TimeUnit.SECONDS);

        try {
            resultObject = pjp.proceed(); //调用对应方法执行
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return resultObject;
    }
}

4.使用redis的getset()来实现分布式锁##

此方法使redisTemplate.boundValueOps(key).getAndSet(value)的方法,如果返回空,表示获取了分布式锁;如果返回不为空,表示分布式锁已经被其他程序占用

5.使用zookeeper的创建节点node

使用zookeeper创建节点node,如果创建节点成功,表示获取了此分布式锁;如果创建节点失败,表示此分布式锁已经被其他程序占用(多个程序同时创建一个节点node,只有一个能够创建成功)

6.使用zookeeper的创建临时序列节点##

使用zookeeper创建临时序列节点来实现分布式锁,适用于顺序执行的程序,大体思路就是创建临时序列节点,找出最小的序列节点,获取分布式锁,程序执行完成之后此序列节点消失,通过watch来监控节点的变化,从剩下的节点的找到最小的序列节点,获取分布式锁,执行相应处理,依次类推......

本文主要介绍了使用redis和zookeeper实现分布式锁的处理,也可以关注我的公众号:不知风在何处,相互沟通,共同进步。

共有 人打赏支持
粉丝 16
博文 10
码字总数 5441
评论 (18)
公孙二狗
// 分布式锁,如果没有此key,设置此值并返回true;如果没有此key,则返回false
// 是不是应该修改为:分布式锁,如果没有此key,设置此值并返回true;如果有此key,则返回false
boolean result = redisTemplate.boundValueOps(key).setIfAbsent(value);
if (!result) {
//其他程序已经获取分布式锁
return resultObject;
}

//设置过期时间,默认一分钟
redisTemplate.boundValueOps(key).expire(expire, TimeUnit.SECONDS);

try {
resultObject = pjp.proceed(); //调用对应方法执行
// 执行完后好像缺少释放锁的代码
} catch (Throwable throwable) {
throwable.printStackTrace();
}
htlr

引用来自“公孙二狗”的评论

// 分布式锁,如果没有此key,设置此值并返回true;如果没有此key,则返回false
// 是不是应该修改为:分布式锁,如果没有此key,设置此值并返回true;如果有此key,则返回false
boolean result = redisTemplate.boundValueOps(key).setIfAbsent(value);
if (!result) {
//其他程序已经获取分布式锁
return resultObject;
}

//设置过期时间,默认一分钟
redisTemplate.boundValueOps(key).expire(expire, TimeUnit.SECONDS);

try {
resultObject = pjp.proceed(); //调用对应方法执行
// 执行完后好像缺少释放锁的代码
} catch (Throwable throwable) {
throwable.printStackTrace();
}
分布式锁的注释是写错了,我写的这个分布式锁主要是为了定时任务不执行多次,所有对redis key 设置了一个超时时间来使key自动删除
huihrt
对于这种不需要高性能锁的需求,还是用zookeeper比较稳妥一点。
leslie1280
炸了?打不开了
leslie1280
炸了?打不开了
我想有个家
总是把zookeeper看成@Zoker ,我......
丁富贵
直接看Curator和Redisson源码就好了
crazymus
博主写得不错!有个问题请教下,redis单机无法承受大量写入操作时,该如何解决呢?例如秒杀、抢购这样的场景,短时间内会产生大量获取锁的操作。
沧海_Sea

引用来自“huihrt”的评论

对于这种不需要高性能锁的需求,还是用zookeeper比较稳妥一点。
zookeeper 性能高么?
KKiCC
redis或者zk就可以实现分布式锁。
特拉仔

引用来自“crazymus”的评论

博主写得不错!有个问题请教下,redis单机无法承受大量写入操作时,该如何解决呢?例如秒杀、抢购这样的场景,短时间内会产生大量获取锁的操作。
有一种memcache的实现方式,可以百度一下
Mr_Qi
还是有问题吧,如果set完成之后挂掉了,没有执行expire的话
excepiton

引用来自“Mr_Qi”的评论

还是有问题吧,如果set完成之后挂掉了,没有执行expire的话
SET key value [EX seconds] [PX milliseconds] [NX|XX]
xytest01
https://redis.io/topics/distlock
Mr_Qi

引用来自“Mr_Qi”的评论

还是有问题吧,如果set完成之后挂掉了,没有执行expire的话

引用来自“excepiton”的评论

SET key value [EX seconds] [PX milliseconds] [NX|XX]
对的,现在我们也这么用
qwfys
这种想法不错,我回头也试一下。
crazymus

引用来自“crazymus”的评论

博主写得不错!有个问题请教下,redis单机无法承受大量写入操作时,该如何解决呢?例如秒杀、抢购这样的场景,短时间内会产生大量获取锁的操作。

引用来自“特拉仔”的评论

有一种memcache的实现方式,可以百度一下
好的,谢谢提示!
_Moon_
没人发现锁的实现有问题吗?
setIfAbsent和expire在分离的两条语句中,之间crash了就死锁。

https://redis.io/topics/distlock 这里是正确的实现方式
×
htlr
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: