基于redis单节点实现分布式锁
来源:互联网 发布:域名授权系统源码php 编辑:程序博客网 时间:2024/06/05 05:44
分布式锁常见的手段有:基于redis 实现和基于zookeeper实现,小编这里简单的介绍一下采用单节点的redis来实现分布式锁。
基于redis 单节点实现分布式锁:
这里需要声明一下,redis单节点实现的锁存在的弊端有,节点的机器不允许宕机,应该能够想的通。如果各位程序员采用的是集群模式下的redis, 那么用此分布式锁,是会发生 多个客户端都会拥有锁,如果你的业务不强制限制这点,可以继续使用。
楼主环境:
三个节点的sentinel集群 , redis一主一从,java 使用的是 jedisSentinelPool 作为redis 的数据库连接池。
基础铺垫:
因为公司业务的发展时间较短,并发量以及缓存的数据量不是很大,一核一G的虚拟机单节点redis 的qps 读写大约在1-3万,公司系统是四核四G,一主一从完全够用,考虑使用sentinel 来实现99.99%高可用。因为jedisSentinelPool就是通过哨兵来获取主节点的HostAndPost , 所以该模式下 我们能够保证 java中获取的连接一直是:master的连接,即使master宕机了,slave升级到master,我们的分布式锁还是可继续使用,我们的业务不强制要求,锁只能有一个线程持有。
准备:
lua 脚本 unlock.lua 放到你项目的资源目录下,一般为:src/main/resource . 脚本内容如下:
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1])else return 0end
本地线程 和 redis 固定key :
private static final String LOCK_NODE ="LOCK";private static ThreadLocal<String> local = new ThreadLocal<>();
获取锁的核心代码:
/** * agui 获取分布式锁 * 弊端:只能是redis 单节点好用 所以要保证 该节点不能挂掉 * @return */ public static boolean getLock() { Jedis jedis = null; try { jedis = jedisSentinelPool.getResource(); String value = UUID.randomUUID().toString(); String ret = jedis.set(LOCK_NODE, value, "NX", "PX", 10000); if(!StringUtils.isEmpty(ret) && ret.equals("OK")){ local.set(value); return true; } } catch (Exception e) { logger.error("Cache获取锁失败:" + e); return false; } finally { releaseResource(jedis); } return false; }
锁的释放:
用 spring 提供的org.springframework.util.FileCopyUtils 来读取 unlock.lua 中的脚本代码,用jedis.evals()去执行。
/** * agui * * 释放锁 * */ public static void releaseLock() { String script =null; Jedis jedis = null ; try { script = FileCopyUtils.copyToString(new FileReader(ResourceUtils.getFile("classpath:unlock.lua"))); jedis = jedisSentinelPool.getResource(); List<String> keys = new ArrayList<String>(); keys.add(LOCK_NODE); List<String> args = new ArrayList<String>(); args.add(local.get()); jedis.eval(script, keys, args); } catch (IOException e) { logger.error("读取lua 脚本失败 :" + e.getMessage()); } catch (Exception e) { logger.error("Cache释放锁失败:" + e); } finally { releaseResource(jedis); } System.out.println(script); }
/** * 释放redis资源 * * @param jedis */ private static void releaseResource(Jedis jedis) { if (jedis != null) { jedis.close();// jedisSentinelPool.destroy(); // jedisSentinelPool.returnResource(jedis); } }
jedisSentinelPool 配置 信息 :
因为楼主用的此连接池,所以通过getResource()该方法来获取jedis对象,不一定非得跟楼主一致都用哨兵连接池,能够获取到jedis 对象就好。
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="minEvictableIdleTimeMillis" value="60000" /> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <property name="numTestsPerEvictionRun" value="-1" /> <property name="maxTotal" value="100" /> <property name="maxIdle" value="50" /> <property name="minIdle" value="10" /> <property name="maxWaitMillis" value="1500"/> </bean> <bean id="jedisSentinelPool" class="redis.clients.jedis.JedisSentinelPool" destroy-method="destroy"> <constructor-arg name="masterName"> <value>master1</value> </constructor-arg> <constructor-arg name="sentinels"> <set value-type="java.lang.String"> <value>${redis.cluster.host1}</value> <value>${redis.cluster.host2}</value> </set> </constructor-arg> <constructor-arg name="password"> <value>${redis.auth.password}</value> </constructor-arg> <constructor-arg name="poolConfig" ref="jedisPoolConfig" /> </bean> <bean id="redisUtils" class="com.bigpay.common.core.cache.redis.RedisUtils"> <property name="jedisSentinelPool" ref="jedisSentinelPool" /> </bean>
说明:
细心的程序员会发现,我们getLock()返回的是 boolean 类型的值,这点说明我们的锁为乐观锁,它并不会去阻塞等待锁的释放,另外因为:采用的是 此方法jedis.set(LOCK_NODE, value, “NX”, “PX”, 10000); 所以不存在当获得锁的线程A出现延迟,A锁因为过期时间到了而被释放,此时又有线程B获取到了锁,A因为走完逻辑代码之后要进行释放锁,此时的锁是线程B的,如果不用 NX 参数去限制,那么会存在A把B的锁给释放掉了, 希望此文章对你们有所帮助。
- 基于redis单节点实现分布式锁
- 基于单Redis节点的分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 分布式锁实现(基于redis)
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- 基于Redis实现分布式锁
- InnoDB存储引擎--2、存储过程
- Qt之QCheckBox
- 美团一面记录(2017-09-07)
- 【n阶乘求尾数有多少个0】
- Java多线程--CompletionService的使用
- 基于redis单节点实现分布式锁
- 极光推送
- 超大数据量存储常用数据库分表分库算法总结
- 1002. 写出这个数 (20)
- TCP粘包问题分析和解决
- elasticsearch centos7集群部署案例
- bzoj 1458(最小割)
- 测试用例编写(功能测试框架)
- leetcode 32 Longest Valid Parentheses