• php+redis+lua实现分布式锁


    以下是我在工作中用到的类,redis加锁两种方式,解锁为了保证原子性所以只用lua+redis的方式

    缺陷:虽然死锁问题解决了,但业务执行时间超过锁有效期还是存在多客户端加锁问题。
    不过,这个类已经满足了我现在的业务需求

    更优的解决方案可以参考以下两篇文章:
    https://redis.io/topics/distlock (Redlock的算法描述)
    https://mp.weixin.qq.com/s/1bPLk_VZhZ0QYNZS8LkviA

    相关文章  redis 分布式锁的 5个坑,真是又大又深

    Redlock-php 实现了分布式锁

    主从复制 用红锁解决

    代码实现:

    class RedisLock
    {
        /**
         * @var 当前锁标识,用于解锁
         */
        private $_lockFlag;
    
        private $_redis;
    
        public function __construct($host = '127.0.0.1', $port = '6379', $passwd = '')
        {
            $this->_redis = new Redis();
            $this->_redis->connect($host, $port);
            if ($passwd) {
                $this->_redis->auth($passwd);
            }
        }
    
        public function lock($key, $expire = 5)
        {
            $now= time();
            $expireTime = $expire + $now;
            if ($this->_redis->setnx($key, $expireTime)) {
                $this->_lockFlag = $expireTime;
                return true;
            }
    
            // 获取上一个锁的到期时间
            $currentLockTime = $this->_redis->get($key);
            if ($currentLockTime < $now) {
                /* 用于解决
                C0超时了,还持有锁,加入C1/C2/...同时请求进入了方法里面
                C1/C2都执行了getset方法(由于getset方法的原子性,
                所以两个请求返回的值必定不相等保证了C1/C2只有一个获取了锁) */
                $oldLockTime = $this->_redis->getset($key, $expireTime);
                if ($currentLockTime == $oldLockTime) {
                    $this->_lockFlag = $expireTime;
                    return true;
                }
            }
    
            return false;
        }
    
        public function lockByLua($key, $expire = 5)
        {
            $script = <<<EOF
    
                local key = KEYS[1]
                local value = ARGV[1]
                local ttl = ARGV[2]
    
                if (redis.call('setnx', key, value) == 1) then
                    return redis.call('expire', key, ttl)
                elseif (redis.call('ttl', key) == -1) then
                    return redis.call('expire', key, ttl)
                end
    
                return 0
    EOF;
    
            $this->_lockFlag = md5(microtime(true));
            return $this->_eval($script, [$key, $this->_lockFlag, $expire]);
        }
    
        public function unlock($key)
        {
            $script = <<<EOF
    
                local key = KEYS[1]
                local value = ARGV[1]
    
                if (redis.call('exists', key) == 1 and redis.call('get', key) == value) 
                then
                    return redis.call('del', key)
                end
    
                return 0
    
    EOF;
    
            if ($this->_lockFlag) {
                return $this->_eval($script, [$key, $this->_lockFlag]);
            }
        }
    
        private function _eval($script, array $params, $keyNum = 1)
        {
            $hash = $this->_redis->script('load', $script);
            return $this->_redis->evalSha($hash, $params, $keyNum);
        }
    
    }
    
    $redisLock = new RedisLock();
    
    $key = 'lock';
    if ($redisLock->lockByLua($key)) {
        // to do...
        $redisLock->unlock($key);
    }
  • 相关阅读:
    The Quad
    将OrCAD Capture CIS的设计文件(.dsn)导入到PADS Logic VX.2.3
    OrCAD Capture CIS 16.6 将版本16.6的设计文件另存为版本16.2的设计文件
    Eclipse IDE 添加jar包到Java工程中
    PADS Logic VX.2.3 修改软件界面语言
    切换Allegro PCB Editor
    Allegro PCB Design GXL (legacy) 将brd文件另存为低版本文件
    Allegro PCB Design GXL (legacy) 设置自动保存brd文件
    Could not create an acl object: Role '16'
    windows 下apache开启FastCGI
  • 原文地址:https://www.cnblogs.com/xiangshihua/p/15008013.html
Copyright © 2020-2023  润新知