php 使用redis锁限制并发访问类
1.并發(fā)訪問限制問題
對于一些需要限制同一個用戶并發(fā)訪問的場景,如果用戶并發(fā)請求多次,而服務(wù)器處理沒有加鎖限制,用戶則可以多次請求成功。
例如換領(lǐng)優(yōu)惠券,如果用戶同一時間并發(fā)提交換領(lǐng)碼,在沒有加鎖限制的情況下,用戶則可以使用同一個換領(lǐng)碼同時兌換到多張優(yōu)惠券。
偽代碼如下:
if A(可以換領(lǐng))B(執(zhí)行換領(lǐng))C(更新為已換領(lǐng)) D(結(jié)束)?
?
如果用戶并發(fā)提交換領(lǐng)碼,都能通過可以換領(lǐng)(A)的判斷,因為必須有一個執(zhí)行換領(lǐng)(B)后,才會更新為已換領(lǐng)(C)。因此如果用戶在有一個更新為已換領(lǐng)之前,有多少次請求,這些請求都可以執(zhí)行成功。?
2.并發(fā)訪問限制方法
使用文件鎖可以實現(xiàn)并發(fā)訪問限制,但對于分布式架構(gòu)的環(huán)境,使用文件鎖不能保證多臺服務(wù)器的并發(fā)訪問限制。
Redis是一個開源的使用ANSI C語言編寫、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫,并提供多種語言的API。?
本文將使用其setnx方法實現(xiàn)分布式鎖功能。setnx即Set?it?N**ot eX**ists。?
當(dāng)鍵值不存在時,插入成功(獲取鎖成功),如果鍵值已經(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 startprivate $_config;private $_redis;/*** 初始化* @param Array $config redis連接設(shè)定*/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)突然出錯導(dǎo)致死鎖,所以在獲取鎖的時候增加一個過期時間,如果已超過過期時間,即使是鎖定狀態(tài)都會釋放鎖,避免死鎖導(dǎo)致的問題。?
總結(jié)
以上是生活随笔為你收集整理的php 使用redis锁限制并发访问类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: React Native之AppRegi
- 下一篇: PHP读取微信超时,'curl出错,错误