この記事では、redis lockを使用して同時アクセスを制限するPHPクラスを紹介し、同時アクセスの制限方法を詳しく紹介します。
1. 同時アクセス制限の問題
同じユーザーによる同時アクセスを制限する必要がある一部のシナリオでは、ユーザーが複数の同時リクエストを実行し、サーバーがロック制限を処理しない場合、ユーザーは複数のリクエストを成功させることができます。
たとえば、クーポンを引き換えるときに、ユーザーが引き換えコードを同時に送信すると、ユーザーは同じ引き換えコードを使用して、制限をロックすることなく同時に複数のクーポンを引き換えることができます。
疑似コードは次のとおりです:
if A (引き換え可能)
B (引き換え実行)
C (引き換えられるように更新)
D (終了)
ユーザーが引き換えコードを同時に送信すると、両方が償還済み(C)に更新される前に償還(B)を実行する必要があるため、償還可能(A)と判断されます。したがって、更新が適用される前にユーザーが複数のリクエストを行った場合、これらのリクエストは正常に実行できます。
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>'; } ?>
テスト方法:
2つの異なるブラウザを開き、AとBのdemo.phpに同時にアクセスします
最初にアクセスすると、ロックを取得します
出力
get lock success
do sth..
success
別のロックの取得に失敗した場合は、同時に 1 つのアクセスのみが有効であることを保証するために、リクエストの頻度が高すぎます
が出力されます。同時アクセス。
突発的なシステムエラーによるデッドロックを回避するため、ロック取得時に有効期限を付加し、ロック状態でも有効期限を過ぎるとロックが解除されます。
redis ロックを使用して同時アクセスを制限するクラスの例に関する PHP 関連の記事をさらに詳しく知りたい場合は、PHP 中国語 Web サイトに注目してください。