zoukankan      html  css  js  c++  java
  • 基于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);
        }
    
    }
    
    
    
    
    }
  • 相关阅读:
    FaceBook API
    CAP – Consistency, Availability, Partition Tolerance
    Hypothesis Testing
    MOSS 2007中如何添加VariationsLabelMenu来在不同语言的variation间切换?
    用Windows 2008 R2做工作机遇到的IE的安全问题
    如何使用Notepad++快速整理挤在一起的CallStack信息(将换行符作为被替换的内容)
    Managed Metadata Service介绍系列 之四
    MOSS 2007捞取ConfigDB中的数据, 得到内部名所对应的timer job的title及运行状况
    Log Parser分析IIS log的举例
    EventCache表太大, 怎么办?
  • 原文地址:https://www.cnblogs.com/huxuhong/p/12871069.html
Copyright © 2011-2022 走看看