zoukankan      html  css  js  c++  java
  • redis分布式锁实现

    public interface DistributedLock {
        /**
         * 加锁
         * @param lockKey 锁定的key
         * @param lockSeconds  锁定时间(单位:秒),超过该锁定时间会自动释放锁,可能会导致并发问题。
         * @param expirySeconds  本次获取锁请求失效时间(单位:秒)
         * @param sleepMillisecond 本次获取锁失败,等到多少毫秒再次尝试获取(单位:毫秒)
         * @return 返回值大于0,则说明获取到锁,调用者需要保留该值,并在解锁时传入。
         */
        public Long lock(String lockKey,long lockSeconds, long expirySeconds, int sleepMillisecond);
        
        /**
         * 解锁
         * @param lockKey 锁定的key
         */
        public void unlock(String lockKey,long lockValue);
    }
    import java.util.concurrent.TimeUnit;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    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.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    
    import com.aaron.redis.lock.DistributedLock;
    
    @Service("redisDistributedLock")
    public class RedisDistributedLockImpl implements DistributedLock {
        private static final Logger LOG = LoggerFactory.getLogger(RedisDistributedLockImpl.class);
    
        /**
         * 锁定超时时间60秒, 单位毫秒, 意味着加锁期间内执行完操作 如果未完成会自动过期释放锁,造成并发问题。
         */
        private static final long DEFAULT_LOCK_TIMEOUT = 60 * 1000;
    
        @Autowired
        protected StringRedisTemplate stringRedisTemplate;
    
        @Override
        public Long lock(String lockKey, long lockSeconds, long expirySeconds, int sleepMillisecond) {
            Long lockVal = 0L;
            long currMs = System.currentTimeMillis();
            if (lockSeconds > 0L) {
                lockSeconds = lockSeconds * 1000;
            } else {
                lockSeconds = DEFAULT_LOCK_TIMEOUT;
            }
            while (true) {
                Long nextmin = getNextMin();
    
                boolean lockResult = stringRedisTemplate.execute(new RedisCallback<Boolean>() {
                    @Override
                    public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                        return connection.setNX(lockKey.getBytes(), String.valueOf(nextmin).getBytes());
                    }
                });
                if (lockResult) {// 获得锁
                    LOG.debug("+++key:{} lock success", lockKey);
                    stringRedisTemplate.expire(lockKey, lockSeconds, TimeUnit.MILLISECONDS); // 设置超时时间,释放内存
                    lockVal = nextmin;
                    break;
                } else {
                    // 调用get
                    String lockTimeObj = stringRedisTemplate.opsForValue().get(lockKey); // redis里的时间
    
                    if (null != lockTimeObj) {
                        Long ts = Long.valueOf(lockTimeObj);
                        long nowTs = System.currentTimeMillis();
                        if (nowTs - ts >= 0) {// 锁已经失效
                            // 调用getset,获取旧值,写入新值,获取不到值为null
                            long nextminVal = getNextMin();
                            String lockTimeObj1 = stringRedisTemplate.opsForValue().getAndSet(lockKey,
                                    String.valueOf(nextminVal));
                            if (null == lockTimeObj1 || lockTimeObj.equals(lockTimeObj1)) {
                                stringRedisTemplate.expire(lockKey, lockSeconds, TimeUnit.MILLISECONDS); // 设置超时时间,释放内存
                                lockVal = nextminVal;
                                break;
                            }
                        }
                    }
    
                    if (lockVal == 0L) {
                        if (checkTimeout(currMs, expirySeconds)) {
                            break;
                        }
                        sleep(sleepMillisecond);
                    }
                }
            }
            return lockVal;
        }
    
        @Override
        public void unlock(String lockKey, long lockValue) {
            if (lockValue <= 0L) {
                return;
            }
            String objVal = stringRedisTemplate.opsForValue().get(lockKey);
            if (objVal != null && objVal.equals(String.valueOf(lockValue))) {
                stringRedisTemplate.delete(lockKey); // 删除键
                LOG.debug("+++key:{} release success", lockKey);
            }
        }
    
        private boolean checkTimeout(long currMs, long expirySeconds) {
            return (System.currentTimeMillis() - currMs) > (expirySeconds * 1000);
        }
    
        private long getNextMin() {
            return System.currentTimeMillis() + 60000;
        }
    
        private void sleep(int ms) {
            try {
                if (ms <= 0) {
                    ms = 50;
                }
                Thread.sleep(ms);
            } catch (InterruptedException e) {
            }
        }
    
    }

            //lockKey
            String lockKey = "lockKey"; //key
            //获取到锁,则锁定30秒
            long lockSeconds = 30;
            //请求200秒如果还没获取到锁就超时返回
            long expirySeconds = 0;
            //未获取到锁,则休息30毫秒然后重试,直到expirySeconds过期
            int sleepMillisecond = 30;
            //获取锁
            long lockVal = redisDistributedLock.lock(lockKey, lockSeconds, expirySeconds, sleepMillisecond);
            //获取锁成功
            if(lockVal > 0L){
                try{
              //执行逻辑
           }catch (Exception e){
    }
    finally{ //释放锁 redisDistributedLock.unlock(lockKey, lockVal); } }else{ //获取锁失败 } }
  • 相关阅读:
    Linkerd 2.10(Step by Step)—将 GitOps 与 Linkerd 和 Argo CD 结合使用
    Linkerd 2.10(Step by Step)—多集群通信
    Linkerd 2.10(Step by Step)—使用 Kustomize 自定义 Linkerd 的配置
    Linkerd 2.10(Step by Step)—控制平面调试端点
    Linkerd 2.10(Step by Step)—配置超时
    Linkerd 2.10(Step by Step)—配置重试
    Linkerd 2.10(Step by Step)—配置代理并发
    本地正常运行,线上环境诡异异常原因集合
    Need to invoke method 'xxx' declared on target class 'yyy', but not found in any interface(s) of the exposed proxy type
    alpine 安装常用命令
  • 原文地址:https://www.cnblogs.com/aaronzheng/p/12106064.html
Copyright © 2011-2022 走看看