程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP綜合 >> php 使用redis鎖限制並發訪問類示例

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

編輯:PHP綜合

本文介紹了php 使用redis鎖限制並發訪問類,並詳細的介紹了並發訪問限制方法。

1.並發訪問限制問題

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

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

偽代碼如下:

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

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

2.並發訪問限制方法

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

Redis是一個開源的使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日志型、Key-Value數據庫,並提供多種語言的API。

本文將使用其setnx方法實現分布式鎖功能。setnx即Set it N**ot eX**ists。

當鍵值不存在時,插入成功(獲取鎖成功),如果鍵值已經存在,則插入失敗(獲取鎖失敗)

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

<?php
require '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. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved