分布式应用下的Redis单机锁设计与实现
2016-04-18 17:25
381 查看
背景
最近写了一个定时任务,期望是同一时间只有一台机器运行即可。因为是应用是在集群环境下跑的,所以需要自己实现类一个简陋的Redis单机锁。原理
主要是使用了Redis的SET NX特性,成功设置的那个客户端则被认为拿到了锁,没有设置成功的其他客户单则认为没有拿到锁。在分布式环境下使用锁是挺危险的一件事情,我们可能会遇到一些问题:
Redis单点故障;
应用与Redis网络不通;
应用异常导致锁没有得到释放;
误操作锁。
对于问题1,避免Redis单点故障,可以使用Redis分布式锁的实现,提供了多种语言的开源实现(http://redis.io/topics/distlock),也可以使用其他配置管理类组件实现(比如:zk、consul、etcd等),不在此文讨论。
对于问题2,我们需要把业务限定在不强依赖Redis锁的范围,虽然绝大多数情况不会发生问题,但是不能完全保证Redis锁不出问题。
对于问题3,为了防止异常引起的死锁情况,需要为每个锁设置超时时间,以确保不会因为应用问题导致无法释放锁。同时要设置一个合理的超时时间以免,达不到或者削弱锁的效果。
对于问题4,为每个锁存储一个标记,当解锁的时候,进行验证,用以保证每个客户端只能操作自己的锁。
实现
public class RedisLock { private static final Logger logger = LoggerFactory.getLogger(RedisLocker.class); private final Map<String, String> LOCK_MAP = new HashMap<String, String>(4); private JedisPool pool; public enum EXPX { EX,PX } public synchronized boolean tryAcquire(String topic, EXPX expx,long time) throws Exception{ if (!LOCK_MAP.containsKey(topic)) { String uuid = UUID.randomUUID().toString(); Jedis jedis = null; // 存在key,返回空,不存在返回OK try { jedis = pool.getResource(); String result = jedis.set(topic, uuid, "NX", expx.name(), time); logger.error("获取返回值,result {}", result); if ("OK".equals(result)) { LOCK_MAP.put(topic, uuid); return true; } logger.error("获取锁失败,result {}", result); } catch (Exception e) { pool.returnBrokenResource(jedis); throw e; }finally { pool.returnResource(jedis); } } return false; } public boolean unlock(String topic) { Jedis jedis = null; try { jedis = pool.getResource(); String random = jedis.get(topic); if (random != null && random.equals(LOCK_MAP.get(topic))) { jedis.del(topic); } LOCK_MAP.remove(topic); } catch (Exception e) { pool.returnBrokenResource(jedis); throw e; } finally { pool.returnResource(jedis); } return true; } public void setPool(JedisPool pool) { this.pool = pool; } }
总结
本文做了一种简单暴力的锁的实现。没有做高可用的方案,主要胜在轻量,简单,在平时场景可以使用。除了使用配置中心相关的组件也是可以实现锁的,并且是分布式的锁。
关于RedisPool的两点需要注意:
在锁的场景,testOnBorrow应该为true;
设置合理的空闲连接数;
设置连接上限,防止占用过多资源。
相关文章推荐
- 跟我学REDIS-REDIS(一)----安装
- 跟我学REDIS-REDIS(二)----常用数据类型之string
- 在c#程序中初步使用redis
- Ubuntu下设置redis让其他服务器访问
- Redis及其应用
- [Redis]c# redis缓存辅助类
- Redis 部署安装
- Redis入门(一)系统安装
- Centos7 安装Redis和Hiredis
- python脚本监控redis制作zabbix模板
- php-redis中文文档(转)
- 【redis学习】Redis数据库入门
- 王高利:CentOS6.5下redis安装部署配置指南、常用命令、主从同步集群、redis-php学习资料整合详解
- Redis的概述和简单使用
- redis结构分析——RDB文件
- python操作redis-hash
- redis结构分析——ziplist
- Java中使用Jedis操作Redis
- 关于redis分布式锁的使用
- redis来共享各个服务器的session,并同时通过redis来缓存一些常用的资源,加快用户获得请求资源的速度(转)