php 使用redis鎖限制并發(fā)訪問類示例
本文介紹了php 使用redis鎖限制并發(fā)訪問類,并詳細的介紹了并發(fā)訪問限制方法。
1.并發(fā)訪問限制問題
對于一些需要限制同一個用戶并發(fā)訪問的場景,如果用戶并發(fā)請求多次,而服務器處理沒有加鎖限制,用戶則可以多次請求成功。
例如換領優(yōu)惠券,如果用戶同一時間并發(fā)提交換領碼,在沒有加鎖限制的情況下,用戶則可以使用同一個換領碼同時兌換到多張優(yōu)惠券。
偽代碼如下:
if A(可以換領)
B(執(zhí)行換領)
C(更新為已換領)
D(結(jié)束)
如果用戶并發(fā)提交換領碼,都能通過可以換領(A)的判斷,因為必須有一個執(zhí)行換領(B)后,才會更新為已換領(C)。因此如果用戶在有一個更新為已換領之前,有多少次請求,這些請求都可以執(zhí)行成功。
2.并發(fā)訪問限制方法
使用文件鎖可以實現(xiàn)并發(fā)訪問限制,但對于分布式架構(gòu)的環(huán)境,使用文件鎖不能保證多臺服務器的并發(fā)訪問限制。
Redis是一個開源的使用ANSI C語言編寫、支持網(wǎng)絡、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫,并提供多種語言的API。
本文將使用其setnx方法實現(xiàn)分布式鎖功能。setnx即Set it N**ot eX**ists。
當鍵值不存在時,插入成功(獲取鎖成功),如果鍵值已經(jīng)存在,則插入失?。ǐ@取鎖失敗)
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);
}
/**
* 創(chuàng)建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,
);
// 創(chuàng)建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
保證同一時間只有一個訪問有效,有效限制并發(fā)訪問。
為了避免系統(tǒng)突然出錯導致死鎖,所以在獲取鎖的時候增加一個過期時間,如果已超過過期時間,即使是鎖定狀態(tài)都會釋放鎖,避免死鎖導致的問題。
源碼下載地址:點擊查看
相關(guān)文章
php使用ffmpeg獲取視頻信息并截圖的實現(xiàn)方法
這篇文章主要介紹了php使用ffmpeg獲取視頻信息并截圖的實現(xiàn)方法,實例分析了php操作視頻與圖像的相關(guān)技巧,需要的朋友可以參考下2016-05-05
PHP封裝XML和JSON格式數(shù)據(jù)接口操作示例
這篇文章主要介紹了PHP封裝XML和JSON格式數(shù)據(jù)接口操作,結(jié)合實例形式分析了php針對xml與json格式數(shù)據(jù)接口封裝相關(guān)操作技巧,需要的朋友可以參考下2019-03-03
PHP Parser 掃描應用打印輸出結(jié)構(gòu)語句實例
這篇文章主要為大家介紹了PHP Parser 掃描應用打印輸出結(jié)構(gòu)語句實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09

