基于LUA+Redis集群 模式的分布式锁
package com.ppdai.merchant.service.configuration; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; @Component @Slf4j public class RedisManager { private static Long lockExpirseTimeout = 10*1000L; @Autowired private RedisTemplate redisTemplate; public String get(String key){ Object obj = redisTemplate.boundValueOps(key).get(); if(obj == null){ return null; }else{ return String.valueOf(obj); } } public void set(String key,String value){ redisTemplate.boundValueOps(key).set(value); } public void set(String key, String value, Long time, TimeUnit timeUnit){ redisTemplate.boundValueOps(key).set(value,time,timeUnit); } public Boolean exist(String key){ return redisTemplate.hasKey(key); } public Boolean setNx(String key,Object value){ return redisTemplate.boundValueOps(key).setIfAbsent(value); } public String getAndSet(String key,Object value){ Object obj = redisTemplate.boundValueOps(key).getAndSet(value); if(obj == null){ return null; }else{ return String.valueOf(obj); } } public <T> T exeLuaScript(String luaScript, List<String> keys, List<String> args,Class<T> clz){ T t = (T)redisTemplate.execute(new RedisCallback<T>(){ @Override public T doInRedis(RedisConnection redisConnection) throws DataAccessException { Object nativeConnection = redisConnection.getNativeConnection(); if (nativeConnection instanceof JedisCluster) { return (T)((JedisCluster) nativeConnection).eval(luaScript.toString(), keys, args); } // 单机模式 else if (nativeConnection instanceof Jedis) { return (T) ((Jedis) nativeConnection).eval(luaScript.toString(), keys, args); } return null; } }); if(t == null){ throw new RuntimeException("redis model doesn't support luascript"); } return t; } public boolean trylock(String lockKey,String lockValue,Long lockWaitTimeout,Long lockExpirseTimeout){ int timeout = lockWaitTimeout.intValue(); while (timeout >= 0){ String expireTimeout = String.valueOf(lockExpirseTimeout/1000); List<String> keys = new ArrayList<String>(); keys.add(lockKey); List<String> args = new ArrayList<String>(); args.add(lockValue); args.add(expireTimeout); String lockLuaScript = setNxLuaScript(); Long exeResult = exeLuaScript(lockLuaScript,keys,args,Long.class); if (exeResult!=null && exeResult.intValue() == 1){ return true; } String lockTimeStr = get(lockKey); if (lockTimeStr != null && Long.parseLong(lockTimeStr) < System.currentTimeMillis()){ String oldLockTimeStr = getAndSet(lockKey,lockValue); if (oldLockTimeStr != null && oldLockTimeStr.equals(lockTimeStr)){ set(lockKey,lockValue,Long.valueOf(expireTimeout),TimeUnit.SECONDS); return true; } } int sleepTime=new Random().nextInt(10)*100; timeout -= sleepTime; try { log.info("获取redis分布式锁失败,sleep:{}ms后重新获取",sleepTime); Thread.sleep(sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } } return false; } public boolean lock(String lockKey){ Long expires = System.currentTimeMillis() + lockExpirseTimeout + 1; String expiresStr = String.valueOf(expires); String expireTimeout = String.valueOf(lockExpirseTimeout/1000); List<String> keys = new ArrayList<String>(); keys.add(lockKey); List<String> args = new ArrayList<String>(); args.add(expiresStr); args.add(expireTimeout); String lockLuaScript = setNxLuaScript(); if (exeLuaScript(lockLuaScript,keys,args,Long.class) == 1){ return true; } String lockTimeStr = get(lockKey); if (lockTimeStr != null && Long.parseLong(lockTimeStr) < System.currentTimeMillis()){ String oldLockTimeStr = getAndSet(lockKey,expiresStr); if (oldLockTimeStr != null && oldLockTimeStr.equals(lockTimeStr)){ set(lockKey,expiresStr,lockExpirseTimeout/1000,TimeUnit.SECONDS); return true; } } return false; } public void unlock(String lockKey,String oldValue){ String luascript = delLuaScript(); List<String> keys = new ArrayList<String>(); keys.add(lockKey); List<String> args = new ArrayList<String>(); args.add(oldValue); exeLuaScript(luascript,keys,args,Long.class); } private String delLuaScript(){ StringBuffer luascript = new StringBuffer(); luascript.append(" if redis.call('exists',KEYS[1]) == 1 and redis.call('get',KEYS[1]) == ARGV[1] then"); luascript.append(" redis.call('del',KEYS[1]) return 1"); luascript.append(" else return 0 end"); return luascript.toString(); } private String setNxLuaScript(){ StringBuffer luascript = new StringBuffer(); luascript.append(" if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then "); luascript.append(" redis.call('expire',KEYS[1],ARGV[2]) return 1"); luascript.append(" else return 0 end"); return luascript.toString(); } public static void main(String args[]){ RedisManager redisManager = new RedisManager(); //伪代码 Long lockWaitTimeout = 5*1000L; Long lockExpirseTimeout = 10*1000L; String lockKey = "LOCK_KEY"; String lockValue = String.valueOf(System.currentTimeMillis() + lockExpirseTimeout + 1); try{ if(redisManager.trylock(lockKey,lockValue,lockWaitTimeout,lockExpirseTimeout)){ //业务逻辑 } }catch (Exception e){ }finally { redisManager.unlock(lockKey,lockValue); } } }
posted on 2020-05-11 18:45 柠檬糖大人你尽然盗号 阅读(682) 评论(0) 编辑 收藏 举报