php 使用redis鎖限制並發訪問類

來源:互聯網
上載者:User


1.並發訪問限制問題


對於一些需要限制同一個使用者並發訪問的情境,如果使用者並發請求多次,而伺服器處理沒有加鎖限制,使用者則可以多次請求成功。

例如換領優惠券,如果使用者同一時間並發提交換領碼,在沒有加鎖限制的情況下,使用者則可以使用同一個換領碼同時兌換到多張優惠券。

虛擬碼如下:

if A(可以換領)    B(執行換領)    C(更新為已換領)D(結束)

如果使用者並發提交換領碼,都能通過可以換領(A)的判斷,因為必須有一個執行換領(B)後,才會更新為已換領(C)。因此如果使用者在有一個更新為已換領之前,有多少次請求,這些請求都可以執行成功。

2.並發訪問限制方法

使用檔案鎖可以實現並發訪問限制,但對於分布式架構的環境,使用檔案鎖不能保證多台伺服器的並發訪問限制。

Redis是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。
本文將使用其setnx方法實現分布式鎖功能。setnx即Set it Not eXists。
當索引值不存在時,插入成功(擷取鎖成功),如果索引值已經存在,則插入失敗(擷取鎖失敗)

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['host'],            $this->_config['port'],$this->_config['timeout'],            $this->_config['reserved'],$this->_config['retry_interval']);                        if(empty($this->_config['auth'])){                $redis->auth($this->_config['auth']);            }            $redis->select($this->_config['index']);        }catch(RedisException $e){                    throw new Exception($e->getMessage());                    return false;        }                return $redis;    }} // class end?>

demo.php

<?phprequire 'RedisLock.class.php';$config = array(    'host' => 'localhost',    'port' => 6379,    'index' => 0,    'auth' => '',    'timeout' => 1,    'reserved' => NULL,    'retry_interval' => 100,);// 建立redislock對象$oRedisLock = new RedisLock($config);// 定義鎖標識$key = 'mylock';// 擷取鎖$is_lock = $oRedisLock->lock($key, 10);if($is_lock){    echo 'get lock success<br>';    echo 'do sth..<br>';    sleep(5);        echo 'success<br>';        $oRedisLock->unlock($key);    // 擷取鎖失敗    }    else{        echo 'request too frequently<br>';}?>



測試方法:
開啟兩個不同的瀏覽器,同時在A,B中訪問demo.php
如果先訪問的會擷取到鎖
輸出
get lock success
do sth..
success

另一個擷取鎖失敗則會輸出request too frequently

保證同一時間只有一個訪問有效,有效限制並發訪問。


為了避免系統突然出錯導致死結,所以在擷取鎖的時候增加一個到期時間,如果已超過到期時間,即使是鎖定狀態都會釋放鎖,避免死結導致的問題。


源碼下載地址:點擊查看

1.並發訪問限制問題

對於一些需要限制同一個使用者並發訪問的情境,如果使用者並發請求多次,而伺服器處理沒有加鎖限制,使用者則可以多次請求成功。

例如換領優惠券,如果使用者同一時間並發提交換領碼,在沒有加鎖限制的情況下,使用者則可以使用同一個換領碼同時兌換到多張優惠券。

虛擬碼如下:

if A(可以換領)    B(執行換領)    C(更新為已換領)D(結束)

如果使用者並發提交換領碼,都能通過可以換領(A)的判斷,因為必須有一個執行換領(B)後,才會更新為已換領(C)。因此如果使用者在有一個更新為已換領之前,有多少次請求,這些請求都可以執行成功。

2.並發訪問限制方法

使用檔案鎖可以實現並發訪問限制,但對於分布式架構的環境,使用檔案鎖不能保證多台伺服器的並發訪問限制。

Redis是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。
本文將使用其setnx方法實現分布式鎖功能。setnx即Set it Not eXists。
當索引值不存在時,插入成功(擷取鎖成功),如果索引值已經存在,則插入失敗(擷取鎖失敗)

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['host'],            $this->_config['port'],            $this->_config['timeout'],            $this->_config['reserved'],            $this->_config['retry_interval']);                        if(empty($this->_config['auth'])){                $redis->auth($this->_config['auth']);            }            $redis->select($this->_config['index']);        }catch(RedisException $e){            throw new Exception($e->getMessage());                    return false;        }        return $redis;    }} // class end?>

demo.php

<?phprequire 'RedisLock.class.php';$config = array(    'host' => 'localhost',    'port' => 6379,    'index' => 0,    'auth' => '',    'timeout' => 1,    'reserved' => NULL,    'retry_interval' => 100,);// 建立redislock對象$oRedisLock = new RedisLock($config);// 定義鎖標識$key = 'mylock';// 擷取鎖$is_lock = $oRedisLock->lock($key, 10);if($is_lock){    echo 'get lock success<br>';    echo 'do sth..<br>';    sleep(5);        echo 'success<br>';        $oRedisLock->unlock($key);    // 擷取鎖失敗    }    else{        echo 'request too frequently<br>';}?>



測試方法:
開啟兩個不同的瀏覽器,同時在A,B中訪問demo.php
如果先訪問的會擷取到鎖
輸出
get lock success
do sth..
success

另一個擷取鎖失敗則會輸出request too frequently

保證同一時間只有一個訪問有效,有效限制並發訪問。


為了避免系統突然出錯導致死結,所以在擷取鎖的時候增加一個到期時間,如果已超過到期時間,即使是鎖定狀態都會釋放鎖,避免死結導致的問題。


以上就是php 使用redis鎖限制並發訪問類的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.