ホームページ  >  記事  >  バックエンド開発  >  PHP は redis ロックを使用して同時アクセス クラスを制限します

PHP は redis ロックを使用して同時アクセス クラスを制限します

黄舟
黄舟オリジナル
2017-02-17 10:07:051800ブラウズ


1. 同時アクセス制限の問題


同じユーザーによる同時アクセスを制限する必要がある一部のシナリオでは、ユーザーが複数の同時リクエストを実行し、サーバーがロック制限を処理しない場合、ユーザーは複数のリクエストを行うことができます。成功したリクエスト。

たとえば、クーポンを引き換えるときに、ユーザーが引き換えコードを同時に送信すると、ユーザーは同じ引き換えコードを使用して、制限をロックすることなく同時に複数のクーポンを引き換えることができます。

疑似コードは次のとおりです:

if A(可以换领)
    B(执行换领)
    C(更新为已换领)D(结束)

ユーザーが引き換えコードを同時に送信した場合、引き換え済み (C) に更新される前に引き換え (B) を実行する必要があるため、それらはすべて引き換え可能 (A) と判断できます。 。したがって、更新が適用される前にユーザーが複数のリクエストを行った場合、これらのリクエストは正常に実行できます。

2. 同時アクセス制限方法

ファイル ロックを使用すると同時アクセス制限を実現できますが、分散アーキテクチャ環境では、ファイル ロックを使用しても複数のサーバーでの同時アクセス制限を保証できません。

Redis は、ANSI C 言語で書かれたオープンソースのログタイプの Key-Value データベースで、ネットワークをサポートし、メモリベースで永続化でき、複数の言語で API を提供します。
この記事では、setnx メソッドを使用して分散ロック機能を実装します。 setnxSetそれを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[&#39;host&#39;],
            $this->_config[&#39;port&#39;],$this->_config[&#39;timeout&#39;],
            $this->_config[&#39;reserved&#39;],$this->_config[&#39;retry_interval&#39;]);            
            if(empty($this->_config[&#39;auth&#39;])){
                $redis->auth($this->_config[&#39;auth&#39;]);
            }
            $redis->select($this->_config[&#39;index&#39;]);
        }catch(RedisException $e){            
        throw new Exception($e->getMessage());            
        return false;
        }        
        return $redis;
    }

} // class end?>

demo.php

<?php
require &#39;RedisLock.class.php&#39;;
$config = array(    &#39;host&#39; => &#39;localhost&#39;,    &#39;port&#39; => 6379,    &#39;index&#39; => 0,    &#39;auth&#39; => &#39;&#39;,    &#39;timeout&#39; => 1,    
&#39;reserved&#39; => NULL,    &#39;retry_interval&#39; => 100,
);
// 创建redislock对象$oRedisLock = new RedisLock($config);
// 定义锁标识$key = &#39;mylock&#39;;
// 获取锁$is_lock = $oRedisLock->lock($key, 10);
if($is_lock){    
echo &#39;get lock success<br>&#39;;    
echo &#39;do sth..<br>&#39;;
    sleep(5);    
    echo &#39;success<br>&#39;;    
    $oRedisLock->unlock($key);
    // 获取锁失败
    }
    else{    
    echo &#39;request too frequently<br>&#39;;
}?>



テスト方法:
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主義者です。 キー値が存在しない場合は挿入成功(ロック取得成功)、キー値が既に存在する場合は挿入失敗(ロック取得失敗)

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[&#39;host&#39;],
            $this->_config[&#39;port&#39;],
            $this->_config[&#39;timeout&#39;],
            $this->_config[&#39;reserved&#39;],
            $this->_config[&#39;retry_interval&#39;]);            
            if(empty($this->_config[&#39;auth&#39;])){
                $redis->auth($this->_config[&#39;auth&#39;]);
            }
            $redis->select($this->_config[&#39;index&#39;]);
        }catch(RedisException $e){            throw new Exception($e->getMessage());            
        return false;
        }        return $redis;
    }

} // class end?>

demo.php

<?phprequire &#39;RedisLock.class.php&#39;;
$config = array(    &#39;host&#39; => &#39;localhost&#39;,    &#39;port&#39; => 6379,    &#39;index&#39; => 0,    &#39;auth&#39; => &#39;&#39;,    &#39;timeout&#39; => 1,   
 &#39;reserved&#39; => NULL,    &#39;retry_interval&#39; => 100,
);
// 创建redislock对象$oRedisLock = new RedisLock($config);
// 定义锁标识$key = &#39;mylock&#39;;
// 获取锁$is_lock = $oRedisLock->lock($key, 10);
if($is_lock){    
echo &#39;get lock success<br>&#39;;    
echo &#39;do sth..<br>&#39;;
    sleep(5);    
    echo &#39;success<br>&#39;;    
    $oRedisLock->unlock($key);
    // 获取锁失败
    }
    else{    
    echo &#39;request too frequently<br>&#39;;
}?>

テスト方法:

2つの異なるブラウザを開き、AとBのdemo.phpに同時にアクセスします 先にアクセスすると、ロックが取得されます出力

get lock success
do sth . success


別のものがロックの取得に失敗すると、同時に 1 つのアクセスのみが有効になるように
request を出力し、事実上同時アクセスを制限します。 。

突発的なシステムエラーによるデッドロックを回避するため、ロック取得時に有効期限を付加し、ロック状態でも有効期限を経過するとロックが解除されます。




上記は、同時アクセスを制限するために redis ロックを使用する PHP の内容です。さらに関連する内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。