redis哨兵模式使用lua脚本实现分布式锁
来源:互联网 发布:管道保温计算软件 编辑:程序博客网 时间:2024/06/15 22:12
问题假如setnx成功,但是expire的时候,失败了,那么该值就会一直存在,这样会造成大的问题,这个问题怎么解决呢?spring redis和redis包在设置key值的时候,都是先调用setnx设置值,成功就返回1,然后通过Expire设置超时时间,这样会出现一个
我们可以通过redis lua脚本,让设置值和设置超时时间在redis服务端一次执行,就不会造成前面描述的问题。
下面是实现分布式锁的代码,采用的哨兵模式:
redis锁类代码:
package com.XXX.util.redis;import java.util.Random;import org.apache.log4j.Logger;import com.XXX.util.Configure;public class RedisLock {private Logger log = Logger.getLogger(RedisLock.class);public RedisLock() {}public RedisLock(String suffix) {this.key = this.key + ":" + suffix;}private static final Configure INS = Configure.getInstans();/** * 默认超时时间(秒) * 超时会抛弃当前锁里的所有线程 * 必要时需要补偿机制 */public static final long DEFAULT_TIMEOUT = Long.parseLong(INS.getValue("DEFAULT_TIMEOUT")) * 1000L;/** * 锁的超时时间(秒),过期删除 */public static final int LOCK_TIMEOUT = Integer.parseInt(INS.getValue("LOCK_TIMEOUT"));private static final long ONE_MILLI_NANOS = 1000000L;private static final long NANOS_TIMEOUT = DEFAULT_TIMEOUT * ONE_MILLI_NANOS;// 锁状态标志private boolean locked = false;// 加锁标志private static final String LOCKED = "TRUE";private String key = "LOCK";private RedisUtil redisUtil = RedisUtil.getInstance();public static void test() {}private static final Random RANDOM = new Random();public boolean lock() {long nano = System.nanoTime();try {while ((System.nanoTime() - nano) < NANOS_TIMEOUT) {if(redisUtil.setnxAndExpire(key, LOCKED, LOCK_TIMEOUT)) {locked = true;log.info("RedisLock lock : Get Lock key=" + key);return locked;}// 短暂休眠,nano避免出现活锁Thread.sleep(5, RANDOM.nextInt(500));}} catch (Exception e) {log.error("RedisLock lock Get Lock Fail", e);}return false;}// 无论是否加锁成功,必须调用public void unlock() {if (locked) {redisUtil.del(key);log.info("RedisLock unlock : Del Lock key=" + key);}} public static void main(String[] args) { for(int i =0 ;i<100;i++){ RedisLock lock = new RedisLock("test1"); boolean lock1 = lock.lock(); System.out.println(lock1+""+i); } }}
RedisUtil 工具类代码:
package com.XXX.util.redis;import java.util.HashSet;import java.util.Set;import org.apache.commons.lang.StringUtils;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import org.slf4j.LoggerFactory;import com.XXX.util.Configure;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisSentinelPool;public class RedisUtil {private static org.slf4j.Logger log = LoggerFactory.getLogger(RedisUtil.class);private static final String Sentinel = "sentinel";private static Configure INS = Configure.getInstans();private static final String RedisModel = INS.getValue("redis.model");private static final String SentinelsURL = INS.getValue("sentinel.urls");private static final String RedisMaster = INS.getValue("redis.master");private static final String RedisPassword = INS.getValue("redis.password");private static final String MaxTotal = INS.getValue("redis.maxTotal");private static final String MaxIdle = INS.getValue("redis.maxIdle");private static final String MaxWait = INS.getValue("redis.maxWait");private static final String MinEvictableIdle = INS.getValue("redis.minEvictableIdle");private static final String NumTestsPerEvictionRun = INS.getValue("redis.numTestsPerEvictionRun");private static final String TimeBetweenEvictionRunsMillis = INS.getValue("redis.timeBetweenEvictionRunsMillis");private static final String BlockWhenExhausted = INS.getValue("redis.blockWhenExhausted");//设置锁的lua脚本private static final String SETNX_EXPIRE_SCRIPT = "if redis.call('setnx', KEYS[1], KEYS[2]) == 1 then\n"+ "return redis.call('expire', KEYS[1], KEYS[3]);\n" + "end\n" + "return nil;";private static RedisUtil redisUtil = new RedisUtil();private JedisSentinelPool sentinelPool;private RedisUtil() {if (Sentinel.equals(RedisModel)) {initSentinel();}}public static RedisUtil getInstance() {return redisUtil;}private void initSentinel() {String[] url = SentinelsURL.split(",");Set<String> sentinels = new HashSet<String>();for (int i = 0; i < url.length; i++) {sentinels.add(url[i]);}GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();poolConfig.setMaxTotal(Integer.valueOf(MaxTotal));poolConfig.setMaxIdle(Integer.valueOf(MaxIdle));poolConfig.setMaxWaitMillis(Long.valueOf(MaxWait));poolConfig.setMinEvictableIdleTimeMillis(Long.valueOf(MinEvictableIdle));poolConfig.setNumTestsPerEvictionRun(Integer.valueOf(NumTestsPerEvictionRun));poolConfig.setTimeBetweenEvictionRunsMillis(Long.valueOf(TimeBetweenEvictionRunsMillis));poolConfig.setBlockWhenExhausted(Boolean.valueOf(BlockWhenExhausted));poolConfig.setTestOnBorrow(true);sentinelPool = StringUtils.isBlank(RedisPassword) ? new JedisSentinelPool(RedisMaster, sentinels, poolConfig): new JedisSentinelPool(RedisMaster, sentinels, poolConfig, RedisPassword);log.info("init sentinelPool success,the sentinelPool=" + sentinelPool);}private Jedis getJedis() {return getInstance().sentinelPool.getResource();}/** * setnx带过期时间功能 * * @param key * 键名 * @param value * 键值 * @param seconds * 单位秒 * @see redis.clients.jedis.Jedis#setnx(String, String) * @see redis.clients.jedis.Jedis#expire(String, int) * @return 成功返回true,失败false */public boolean setnxAndExpire(String key, String value, int seconds) {Jedis redis = null;try {redis = getJedis();Object result = redis.eval(SETNX_EXPIRE_SCRIPT, 3, key, value, seconds + "");return result != null;} catch (Throwable t) {log.error("setnxAndExpire" + t.toString(), t);return false;} finally {close(redis);}}public Long del(String key) {Jedis redis = null;try {redis = getJedis();return redis.del(key);} catch (Exception t) {log.error("删掉key=" + key + "异常"+t.toString(), t);return -1l;} finally {close(redis);}}private void close(Jedis redis) {if (redis != null) {redis.close();}}@Overridepublic String toString() {return "RedisUtil [sentinelPool=" + sentinelPool + "]";}public static void main(String[] args) {boolean b = RedisUtil.getInstance().setnxAndExpire("test", "true", 100);System.out.println(b);}}
RedisUtil 配置文件:#redis configredis.model=sentinelredis.master=st1redis.password=sentinel.urls=192.168.0.13:26379,192.168.0.13:26380,192.168.0.13:26381redis.maxTotal=50redis.maxIdle=10redis.maxWait=20000redis.minEvictableIdle=300000redis.numTestsPerEvictionRun=3redis.timeBetweenEvictionRunsMillis=60000redis.blockWhenExhausted=true#秒(s)DEFAULT_TIMEOUT=30#秒(s)LOCK_TIMEOUT=2
pom配置:
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.6.2</version></dependency>---------------------------------------------------------------------------版权声明------------------------------------------------------------------------------------------
版权声明:本文为博主原创文章,未经博主允许不得转载。博客地址:http://blog.csdn.net/mr_smile2014
阅读全文
1 0
- redis哨兵模式使用lua脚本实现分布式锁
- 基于Redis Lua脚本实现的分布式锁
- Redis使用一、Redis哨兵模式
- redis中使用java脚本实现分布式锁
- php+redis+lua实现分布式锁
- redis使用Lua脚本
- redis sentinel(哨兵)模式
- redis的哨兵模式
- redis-09-哨兵模式
- redis配置哨兵模式
- redis哨兵模式
- redis主从+哨兵模式
- redis的哨兵模式
- Redis sentinel 哨兵模式
- Redis sentinel 哨兵模式
- Redis哨兵模式
- Redis 哨兵模式详解
- Redis的哨兵模式
- Spring初始化
- CRC循环冗余码
- 【Linux】进程间的关系以及终端的概念
- 人生不是只有努力就够了
- LK概览
- redis哨兵模式使用lua脚本实现分布式锁
- 使用sql生成UUID
- 线程控制
- python3编程入门(1)-算术、字符串与变量
- 通过PropertyDescriptor反映射调用set和get方法
- shell语法及实现进度条
- 分享一个格式化json的工具类,在日志输入里直接可以很方便的看log了
- PHP去重的简单写法
- iOS开发 特殊字符 可以在控制台输出哦