Codis分布式锁

近期一项需求需要使用分布式锁,考虑的方案主要有如下两种:

  1. zookeeper
  2. codis

因为对于zookeeper不是特别熟悉,因此选用了codis,Codis是一个分布式的Redis解决方案,从应用层面上看几乎和redis是一样的,我之所以说是几乎,就是因为这里踩了一个坑!

我们都知道,redis中有事务的概念,对应着有事务的命令:

  1. DISCARD
  2. EXEC
  3. MULTI
  4. UNWATCH
  5. WATCH

本以为codis与redis一样支持事务,毕竟语法层面提供了上述的命令,于是分布式锁第一个版本诞生了:

 1 public boolean lock(String key, String value, int expireTime) {
 2         // 开启事务
 3         Transaction transaction = codis.multi();
 4         // setnx方法:如果已经存在key,则不设置值,并返回0,反之设置value,返回1
 5         Long result = codis.setnx(key, value);
 6         // 如果成功了,则设置过期时间
 7         if (result > 0) {
 8             codis.expire(key, expireTime);
 9         }
10         // 执行上述两个命令
11         transaction.exec();
12         return result > 0;
13     }

主要是运用了setnx、expire方法,逻辑上述注释已做说明不再赘述!

在不考虑宕机的情况,理论上应该是可以的,但是实际上运行却出现如下结果:

io.codis.jodis.bo.UnsupportedMethodException: unsupport multi()

令人沮丧,居然提供命令却不能在运行时支持~

事后查了一下codis的文档,确实是不支持的,没办法只能该用其他方式,最后采用了网络上一种较为合适的方式,代码如下:

 1 public boolean lock(String sign, int expireTime) {
 2         String currentTime = String.valueOf(System.currentTimeMillis() + expireTime * 1000);
 3         Long result =codis.setnx(sign, currentTime);
 4         if (result > 0) {
 5             return true;
 6         }
 7         // 若设置失败,则校验codis中的值是否已经过期(宕机导致未删除)
 8         String cacheTime = codis.get(sign);
 9         if (StringUtils.isNotBlank(cacheTime) && Long.parseLong(cacheTime) < System.currentTimeMillis()) {
10             String oldTime = codis.getSet(sign, currentTime);
11             return cacheTime.equals(oldTime);
12         }
13         return false;
14     }

 

posted @ 2017-07-31 19:53  雨中漫步,惟情而已  阅读(1819)  评论(0编辑  收藏  举报