zoukankan      html  css  js  c++  java
  • 1.Redis Lock

    使用场景

    同步锁,让业务方法在锁设定的时间内是同步执行的

    • redisService.setIfAbsent

    • redisService.expire

    @PostMapping("/update")
    public MMHResponse      saveOrUpdateMemberBaby(@RequestBody SaveOrUpdateMemberBabyForm form){
    		GlobalLock globalLock = null;
    		try {
    			globalLock = GlobalLockRedisImpl.getInstance(key);
    			globalLock.lock();
    		//TODO ...业务方法
    			return SuccessResponse.newInstance();
    		} catch (Exception e) {
    			logger.error("[ebiz-user-service][saveOrUpdateMemberBaby]操作失败,原因{}", StackTraceLogUtil.getStackTraceAsString(e));
    			return ErrorResponse.newInstance("操作失败");
    		} finally {
    			//释放锁
    			if (globalLock != null) {
    				globalLock.unlock();
    			}
    		}
    	}
    

    不难看出,redis不参与业务逻辑代码,仅仅作一个定时的开发,保证在某个时间段内 业务代码不受干扰

    我们再来看一下lock 方法

    GlobalLock
    /**
     * <pre>
     * Example 1: 强制获取锁,会阻塞, 超时失败会抛出异常
     *   GlobalLock lock = GlobalLockRedisImpl.getInstance("local key name")
     *   try{
     *      lock.lock(5);
     *      doSamething()
     *   } catch(GDSystemException e) {
     *       //获取锁异常
     *   } finally {
     *      lock.unlock();
     *   }
     *
     *
     *  Example 2: 尝试获取锁, 失败会直接返回false
     *   GlobalLock lock = ...;
     *      if (lock.tryLock()) {
     *          try {
     *              // manipulate protected state
     *          } finally {
     *              lock.unlock();
     *          }
     *      } else {
     *          // perform alternative actions
     *      }
     * </pre>
     */
    public interface GlobalLock {
    
    
        /**
         * 申请加锁,此操作为阻塞操作,且不响应线程中断
         * 超过指定时间会抛出异常,程序需要对异常处理, 最大锁住时间为1分钟
         *
         * @throws RuntimeException 在指定的时间内获取锁失败
         */
        void lock(int timeout) throws RuntimeException;
    
    
        /**
         * 申请加锁, 最大时间30秒
         *
         * @throws RuntimeException
         */
        void lock() throws RuntimeException;
    
        /**
         * Acquires the lock only if it is free at the time of invocation.
         * <p>
         * <p>Acquires the lock if it is available and returns immediately
         * with the value {@code true}.
         * If the lock is not available then this method will return
         * immediately with the value {@code false}.
         * <p>
         * <p>A typical usage idiom for this method would be:
         * <pre>
         *      Lock lock = ...;
         *      if (lock.tryLock()) {
         *          try {
         *              // manipulate protected state
         *          } finally {
         *              lock.unlock();
         *          }
         *      } else {
         *          // perform alternative actions
         *      }
         * </pre>
         * This usage ensures that the lock is unlocked if it was acquired, and
         * doesn't try to unlock if the lock was not acquired.
         *
         * @return {@code true} if the lock was acquired and
         * {@code false} otherwise
         */
        boolean tryLock();
    
    
        /**
         * 释放锁
         */
        void unlock();
    
    
    }
    

      

    GlobalLockRedisImpl
    /**
     * 基于redis实现的全局锁
     */
    public class GlobalLockRedisImpl implements GlobalLock {
    
        private ILog logger = LogFactory.getLog(GlobalLockRedisImpl.class, LogBusinessModule.TRACE_LOG);
    
        private RedisService redisService;
    
        // timeout(ms)
        private static final int TIME_OUT = 30;
    
        // private Jedis jedis;
        private String key;
    
        // state flag
        private volatile boolean locked = false;
    
    
        private static ConcurrentMap<String, GlobalLock> map = Maps.newConcurrentMap();
    
    
        /**
         * 构造函数
         *
         * @param key
         */
        private GlobalLockRedisImpl(String key) {
            this.key = "_LOCK_" + key;
            this.redisService = (RedisService) MyApplicationContext.getBean("redisService");
        }
    
    
        public static GlobalLock getInstance(String key) {
            GlobalLock globalLock = map.get(key);
            if (globalLock == null) {
                map.put(key, new GlobalLockRedisImpl(key));
                return map.get(key);
            }
            return globalLock;
        }
    
    
        public boolean tryLock() {
            long result = redisService.increment(key);
            boolean success = result <= 1;
            if (!success) {// 锁已被占用,等待释放
                result = redisService.increment(key);
                success = result <= 1;
            }
            if (success) {// 处理锁的自动释放
                redisService.set(key, String.valueOf(result), TIME_OUT);
                locked = true;
                if (logger.isDebugEnabled()) {
                    logger.debug("尝试获取锁{}成功, 锁将在 {} 秒后释放", key, TIME_OUT);
                }
            }
            return success;
        }
    
    
        public void lock(int timeout) throws RuntimeException {
            long nanoTime = System.nanoTime();
            do {
                if (redisService.setIfAbsent(key, key)) {
                    redisService.expire(key, 1, TimeUnit.MINUTES); //设定锁过期时间为1分钟
                    locked = true;
                    if (logger.isDebugEnabled()) {
                        logger.debug("get key: {} lock success! -- {} 毫秒", key, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime));
                    }
                    return;
                }
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } while ((System.nanoTime() - nanoTime) < TimeUnit.SECONDS.toNanos(timeout));
            throw new RuntimeException("获取锁超时, 消耗时间: " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime) + "毫秒");
        }
    
    
        public void lock() throws RuntimeException {
            lock(TIME_OUT);
        }
    
    
        public void unlock() {
            if (locked) {
                redisService.delete(key);
            }
        }
    
    
    }
    

      


    public void lock() throws GDSystemException { lock(30); } public void lock(int timeout) throws GDSystemException { long nanoTime = System.nanoTime(); while(!redisService.setIfAbsent(this.key, this.key)) { try { Thread.sleep(300L); } catch (InterruptedException var5) { var5.printStackTrace(); } if(System.nanoTime() - nanoTime >= TimeUnit.SECONDS.toNanos((long)timeout)) { throw new GDSystemException("获取锁超时, 消耗时间: " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime) + "毫秒"); } } this.redisService.expire(this.key,60, TimeUnit.SECONDS); this.locked = true; if(logger.isDebugEnabled()) { logger.debug("get key: {} lock success! -- {} 毫秒", new Object[]{this.key, Long.valueOf(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime))}); } }

    lock 默认你的时间30s,

    redisService.setIfAbsent(this.key, this.key)
    

    进一步来看setIfAbsent的源码:

    public Boolean setIfAbsent(String key, Object value) {
            return this.getRedisTemplate().opsForValue().setIfAbsent(key, value);
        }
        
          public Boolean setIfAbsent(K key, V value) {
            final byte[] rawKey = this.rawKey(key);
            final byte[] rawValue = this.rawValue(value);
            return (Boolean)this.execute(new RedisCallback<Boolean>() {
                public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                    return connection.setNX(rawKey, rawValue);
                }
            }, true);
        }
    

    对 Redis setNX的这个方法,参考官方文档,判断key是否存在(1/0)

    
    Return value
    Integer reply, specifically:
    1 if the key was set
    0 if the key was not set
    

    再往下看 redisService.expire 保证key 60s有效,足够执行业务代码,

    执行往后,delete该key

  • 相关阅读:
    钟国晨160809323(作业5)
    12
    11
    第九次
    8作业
    第七次作业
    6
    林昊5
    计算机网络原理与应用笔记 3/29
    计算机网络原理与应用笔记 3/22
  • 原文地址:https://www.cnblogs.com/Profound/p/8762711.html
Copyright © 2011-2022 走看看