现在的态度决定你未来的高度

使用Redis锁来实现防刷机制,redis并发锁(复制可用)

    /**
     * 申请全局排他锁
     * @param string $name 锁名称
     * @param int $ttl 锁失效时间,避免死锁
     * @param bool $should_block 是否阻塞,非阻塞锁获取失败即返回,阻塞锁会反复申请直到成功
     * @throws GusException
     * @return boolean
     */
    function acquire_global_lock($name, $ttl, $should_block = true){
        if(!is_string($name)){
            throw new GusException('全局锁名称必须为字符串');
        }
        $name = trim($name);
        if(empty($name)){
            throw new GusException('全局锁名称不能为空');
        }
        if(!is_int($ttl) || $ttl < 1){
            throw new GusException('全局锁生存时间必须为正整数');
        }
        $lock = "PUAS_GLOBAL_LOCK_{$name}";
        while(true){
            $expire = time() + $ttl + 1;
            $ret = Redis::setnx($lock, $expire);
            // 获取失败
            if($ret == 0){
                // 阻塞锁
                $old_expire = Redis::get($lock);
                $sleep = $old_expire - time();
                if($sleep > 0){
                    if($should_block){
                        sleep(rand(1, $sleep));
                        continue;
                    } else{
                        // 非阻塞锁,直接失败
                        return false;
                    }
                } else{
                    $new_expire = Redis::getset($lock, time() + $ttl + 1);
                    if($new_expire != $old_expire){
                        continue;
                    }
                }
            }
            // 获取成功
            break;
        }
        // 脚本结束时,释放锁
        register_shutdown_function(
            function ($expire, $lock){
                // 如果锁没过期,主动释放,否则不应释放避免已被其他进程锁定
                if(time() < $expire){
                    Redis::del($lock);
                }
            }, $expire, $lock);
        return true;
    }
    /**
     * 释放全局锁
     * @param string $name
     */
    function release_global_lock($name){
        Redis::del("PUAS_GLOBAL_LOCK_{$name}");
    }
发表新评论