>  기사  >  백엔드 개발  >  PHP는 redis 잠금을 사용하여 동시 액세스 클래스를 제한합니다.

PHP는 redis 잠금을 사용하여 동시 액세스 클래스를 제한합니다.

黄舟
黄舟원래의
2017-02-17 10:07:051799검색


1. 동시 접속 제한 문제


동일한 사용자의 동시 접속을 제한해야 하는 일부 시나리오의 경우 사용자는 동시 요청 횟수가 많고 서버 처리에 대한 잠금 제한이 없으며 사용자는 여러 번 성공적으로 요청할 수 있습니다.

예를 들어 쿠폰 사용 시 사용자가 동시에 사용 코드를 제출하면 동일한 사용 코드를 사용하여 잠금 제한 없이 동시에 여러 쿠폰을 사용할 수 있습니다.

의사 코드는 다음과 같습니다.

if A(可以换领)
    B(执行换领)
    C(更新为已换领)D(结束)

사용자가 상환 코드를 동시에 제출하면 모두 상환 가능(A) 판정을 통과할 수 있습니다. 구속(B)을 수행하는 사람이 되면 구속(C)으로 업데이트됩니다. 따라서 업데이트가 회수되기 전에 사용자가 여러 요청을 하면 해당 요청이 성공적으로 실행될 수 있습니다.

2. 동시 접근 제한 방법

파일 잠금을 사용하면 동시 접근 제한이 가능하지만, 분산 아키텍처 환경에서는 파일 잠금을 사용하면 여러 서버의 보안을 보장할 수 없습니다. 동시 액세스 제한.

Redis는 ANSI C 언어로 작성된 오픈소스 로그형 Key-Value 데이터베이스로, 네트워크를 지원하고, 메모리 기반 및 영속성이 있으며, 다국어로 API를 제공합니다.
이 기사에서는 setnx 메소드를 사용하여 분산 잠금 기능을 구현합니다. setnxSetNX하지 않습니다.
키 값이 없으면 삽입 성공(잠금 획득 성공) 키 값이 이미 있으면 삽입 실패(잠금 획득 실패)

RedisLock.class.php

<?php/**
 *  Redis锁操作类
 *  Date:   2016-06-30
 *  Author: fdipzone
 *  Ver:    1.0
 *
 *  Func:
 *  public  lock    获取锁
 *  public  unlock  释放锁
 *  private connect 连接
 */class RedisLock { // class start

    private $_config;    private $_redis;    /**
     * 初始化
     * @param Array $config redis连接设定
     */
    public function __construct($config=array()){
        $this->_config = $config;
        $this->_redis = $this->connect();
    }    /**
     * 获取锁
     * @param  String  $key    锁标识
     * @param  Int     $expire 锁过期时间
     * @return Boolean
     */
    public function lock($key, $expire=5){
        $is_lock = $this->_redis->setnx($key, time()+$expire);        // 不能获取锁
        if(!$is_lock){            // 判断锁是否过期
            $lock_time = $this->_redis->get($key);            // 锁已过期,删除锁,重新获取
            if(time()>$lock_time){
                $this->unlock($key);
                $is_lock = $this->_redis->setnx($key, time()+$expire);
            }
        }        return $is_lock? true : false;
    }    /**
     * 释放锁
     * @param  String  $key 锁标识
     * @return Boolean
     */
    public function unlock($key){        return $this->_redis->del($key);
    }    /**
     * 创建redis连接
     * @return Link
     */
    private function connect(){        try{
            $redis = new Redis();
            $redis->connect($this->_config[&#39;host&#39;],
            $this->_config[&#39;port&#39;],$this->_config[&#39;timeout&#39;],
            $this->_config[&#39;reserved&#39;],$this->_config[&#39;retry_interval&#39;]);            
            if(empty($this->_config[&#39;auth&#39;])){
                $redis->auth($this->_config[&#39;auth&#39;]);
            }
            $redis->select($this->_config[&#39;index&#39;]);
        }catch(RedisException $e){            
        throw new Exception($e->getMessage());            
        return false;
        }        
        return $redis;
    }

} // class end?>

demo.php

<?php
require &#39;RedisLock.class.php&#39;;
$config = array(    &#39;host&#39; => &#39;localhost&#39;,    &#39;port&#39; => 6379,    &#39;index&#39; => 0,    &#39;auth&#39; => &#39;&#39;,    &#39;timeout&#39; => 1,    
&#39;reserved&#39; => NULL,    &#39;retry_interval&#39; => 100,
);
// 创建redislock对象$oRedisLock = new RedisLock($config);
// 定义锁标识$key = &#39;mylock&#39;;
// 获取锁$is_lock = $oRedisLock->lock($key, 10);
if($is_lock){    
echo &#39;get lock success<br>&#39;;    
echo &#39;do sth..<br>&#39;;
    sleep(5);    
    echo &#39;success<br>&#39;;    
    $oRedisLock->unlock($key);
    // 获取锁失败
    }
    else{    
    echo &#39;request too frequently<br>&#39;;
}?>



테스트 방법:
두 개의 다른 브라우저를 열고 A와 B에서 동시에 데모.php에 액세스합니다.
먼저 액세스하면 잠금을 얻습니다.
출력
잠금 성공
do sth.
success

다른 사람이 잠금을 획득하지 못하면 너무 자주 요청

이 출력됩니다. 동시에 하나의 액세스만 유효하도록 하여 동시 액세스를 효과적으로 제한합니다.


갑작스러운 시스템 오류로 인한 교착 상태를 방지하기 위해 잠금 획득 시 만료 시간을 추가합니다. 만료 시간이 지나면 잠금 상태에서도 잠금이 해제됩니다. 교착상태 문제를 피하기 위해.


소스코드 다운로드 주소 : 클릭하시면 보실 수 있습니다

1. 동시 접속 제한 문제

필요한 일부 사용자의 경우 동시성 제한 액세스 시나리오에서 사용자가 여러 개의 동시 요청을 하고 서버 프로세스에 잠금 제한이 없으면 사용자는 여러 개의 성공적인 요청을 할 수 있습니다.

예를 들어 쿠폰 사용 시 사용자가 동시에 사용 코드를 제출하면 동일한 사용 코드를 사용하여 잠금 제한 없이 동시에 여러 쿠폰을 사용할 수 있습니다.

의사 코드는 다음과 같습니다.

if A(可以换领)
    B(执行换领)
    C(更新为已换领)D(结束)

사용자가 상환 코드를 동시에 제출하면 모두 상환 가능(A) 판정을 통과할 수 있습니다. 구속(B)을 수행하는 사람이 되면 구속(C)으로 업데이트됩니다. 따라서 업데이트가 회수되기 전에 사용자가 여러 요청을 하면 해당 요청이 성공적으로 실행될 수 있습니다.

2. 동시 접근 제한 방법

파일 잠금을 사용하면 동시 접근 제한이 가능하지만, 분산 아키텍처 환경에서는 파일 잠금을 사용하면 여러 서버의 보안을 보장할 수 없습니다. 동시 액세스 제한.

Redis는 ANSI C 언어로 작성된 오픈소스 로그형 Key-Value 데이터베이스로, 네트워크를 지원하고, 메모리 기반 및 영속성이 있으며, 다국어로 API를 제공합니다.
이 기사에서는 setnx 메소드를 사용하여 분산 잠금 기능을 구현합니다. setnxSetNX하지 않습니다.
키 값이 없으면 삽입 성공(잠금 획득 성공) 키 값이 이미 있으면 삽입 실패(잠금 획득 실패)

RedisLock.class.php

<?php/**
 *  Redis锁操作类
 *  Date:   2016-06-30
 *  Author: fdipzone
 *  Ver:    1.0
 *
 *  Func:
 *  public  lock    获取锁
 *  public  unlock  释放锁
 *  private connect 连接
 */class RedisLock { // class start

    private $_config;    private $_redis;    /**
     * 初始化
     * @param Array $config redis连接设定
     */
    public function __construct($config=array()){
        $this->_config = $config;
        $this->_redis = $this->connect();
    }    /**
     * 获取锁
     * @param  String  $key    锁标识
     * @param  Int     $expire 锁过期时间
     * @return Boolean
     */
    public function lock($key, $expire=5){
        $is_lock = $this->_redis->setnx($key, time()+$expire);        // 不能获取锁
        if(!$is_lock){            // 判断锁是否过期
            $lock_time = $this->_redis->get($key);            // 锁已过期,删除锁,重新获取
            if(time()>$lock_time){
                $this->unlock($key);
                $is_lock = $this->_redis->setnx($key, time()+$expire);
            }
        }        return $is_lock? true : false;
    }    /**
     * 释放锁
     * @param  String  $key 锁标识
     * @return Boolean
     */
    public function unlock($key){        return $this->_redis->del($key);
    }    /**
     * 创建redis连接
     * @return Link
     */
    private function connect(){        try{
            $redis = new Redis();
            $redis->connect($this->_config[&#39;host&#39;],
            $this->_config[&#39;port&#39;],
            $this->_config[&#39;timeout&#39;],
            $this->_config[&#39;reserved&#39;],
            $this->_config[&#39;retry_interval&#39;]);            
            if(empty($this->_config[&#39;auth&#39;])){
                $redis->auth($this->_config[&#39;auth&#39;]);
            }
            $redis->select($this->_config[&#39;index&#39;]);
        }catch(RedisException $e){            throw new Exception($e->getMessage());            
        return false;
        }        return $redis;
    }

} // class end?>

demo.php

<?phprequire &#39;RedisLock.class.php&#39;;
$config = array(    &#39;host&#39; => &#39;localhost&#39;,    &#39;port&#39; => 6379,    &#39;index&#39; => 0,    &#39;auth&#39; => &#39;&#39;,    &#39;timeout&#39; => 1,   
 &#39;reserved&#39; => NULL,    &#39;retry_interval&#39; => 100,
);
// 创建redislock对象$oRedisLock = new RedisLock($config);
// 定义锁标识$key = &#39;mylock&#39;;
// 获取锁$is_lock = $oRedisLock->lock($key, 10);
if($is_lock){    
echo &#39;get lock success<br>&#39;;    
echo &#39;do sth..<br>&#39;;
    sleep(5);    
    echo &#39;success<br>&#39;;    
    $oRedisLock->unlock($key);
    // 获取锁失败
    }
    else{    
    echo &#39;request too frequently<br>&#39;;
}?>



테스트 방법:
두 개의 다른 브라우저를 열고 A와 B에서 동시에 데모.php에 액세스합니다.
먼저 액세스하면 잠금을 얻습니다.
출력
잠금 성공
do sth.
success

다른 사람이 잠금을 획득하지 못하면 너무 자주 요청

이 출력됩니다. 동시에 하나의 액세스만 유효하도록 하여 동시 액세스를 효과적으로 제한합니다.


갑작스러운 시스템 오류로 인한 교착 상태를 방지하기 위해 잠금 획득 시 만료 시간을 추가합니다. 만료 시간이 지나면 잠금 상태에서도 잠금이 해제됩니다. 교착상태 문제를 피하기 위해.


위 내용은 redis lock을 사용하여 동시 접속을 제한하는 PHP 내용이며, 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php)를 참고하시기 바랍니다. .cn)!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.