同じユーザーによる同時アクセスを制限する必要がある一部のシナリオでは、ユーザーが複数の同時リクエストを実行し、サーバーがロック制限を処理しない場合、ユーザーは複数のリクエストを行うことができます。成功したリクエスト。
たとえば、クーポンを引き換えるときに、ユーザーが引き換えコードを同時に送信すると、ユーザーは同じ引き換えコードを使用して、制限をロックすることなく同時に複数のクーポンを引き換えることができます。
疑似コードは次のとおりです:
if A(可以换领) B(执行换领) C(更新为已换领)D(结束)
ユーザーが引き換えコードを同時に送信した場合、引き換え済み (C) に更新される前に引き換え (B) を実行する必要があるため、それらはすべて引き換え可能 (A) と判断できます。 。したがって、更新が適用される前にユーザーが複数のリクエストを行った場合、これらのリクエストは正常に実行できます。
ファイル ロックを使用すると同時アクセス制限を実現できますが、分散アーキテクチャ環境では、ファイル ロックを使用しても複数のサーバーでの同時アクセス制限を保証できません。
Redis は、ANSI C 言語で書かれたオープンソースのログタイプの Key-Value データベースで、ネットワークをサポートし、メモリベースで永続化でき、複数の言語で API を提供します。
この記事では、setnx メソッドを使用して分散ロック機能を実装します。 setnxはSetそれをXしないX主義者です。
キー値が存在しない場合は挿入成功(ロック取得成功)、キー値が既に存在する場合は挿入失敗(ロック取得失敗)
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>'; }?>
テスト方法:
2つの異なるブラウザを開き、AとBのdemo.phpに同時にアクセスします
先にアクセスすると、ロックが取得されます
出力
get lock success
do sth .
success
別のものがロックの取得に失敗すると、同時に 1 つのアクセスのみが有効になるように request を出力し、事実上同時アクセスを制限します。 。
突発的なシステムエラーによるデッドロックを回避するため、ロック取得時に有効期限を付加し、ロック状態でも有効期限を経過するとロックが解除されます。
ソース コードのダウンロード アドレス: クリックして表示します
1. 同時アクセス制限の問題
疑似コードは次のとおりです:
if A(可以换领) B(执行换领) C(更新为已换领)D(结束)ユーザーが引き換えコードを同時に送信した場合、引き換え済み (C) に更新される前に引き換え (B) を実行する必要があるため、それらはすべて引き換え可能 (A) と判断できます。 。したがって、更新が適用される前にユーザーが複数のリクエストを行った場合、これらのリクエストは正常に実行できます。
2. 同時アクセス制限方法
Redis
は、ANSI C 言語で書かれたオープンソースのログタイプの Key-Value データベースで、ネットワークをサポートし、メモリベースで永続化でき、複数の言語で API を提供します。この記事では、setnx メソッドを使用して分散ロック機能を実装します。 setnxは
SetそれをXしないX主義者です。 キー値が存在しない場合は挿入成功(ロック取得成功)、キー値が既に存在する場合は挿入失敗(ロック取得失敗)
<?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?>
<?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>';
}?>
テスト方法:
2つの異なるブラウザを開き、AとBのdemo.phpに同時にアクセスします 先にアクセスすると、ロックが取得されます出力
get lock success
do sth . success
別のものがロックの取得に失敗すると、同時に 1 つのアクセスのみが有効になるように request を出力し、事実上同時アクセスを制限します。 。
突発的なシステムエラーによるデッドロックを回避するため、ロック取得時に有効期限を付加し、ロック状態でも有効期限を経過するとロックが解除されます。
上記は、同時アクセスを制限するために redis ロックを使用する PHP の内容です。さらに関連する内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。