Codis分布式锁
近期一项需求需要使用分布式锁,考虑的方案主要有如下两种:
- zookeeper
- codis
因为对于zookeeper不是特别熟悉,因此选用了codis,Codis是一个分布式的Redis解决方案,从应用层面上看几乎和redis是一样的,我之所以说是几乎,就是因为这里踩了一个坑!
我们都知道,redis中有事务的概念,对应着有事务的命令:
- DISCARD
- EXEC
- MULTI
- UNWATCH
- 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 }