PHP 内存缓存设置函数锁


php memcached set function lock

我用php memcached来实现令牌 代码如下:

    function addTokenKey($token)
    {
       $allTokens = $this->memcache->get("AllTokens");

        if(gettype($allTokens) == "boolean")
        {
            $array = array();
            array_push($array,$token);
            $this->memcache->set("AllTokens",$array);
            echo "addTokenKey 1.2:".count($array)."<br>";
        }
        else{                
            echo "addTokenKey 2.1:".count($allTokens)."<br>";
            array_push($allTokens,$token);
            $this->memcache->set("AllTokens",$allTokens);   
            echo "addTokenKey 2.2:".count($allTokens)."<br>";
        }
     } 

我发送多个请求以同时调用此函数

但有时我得到相同的结果,例如:

请求结果

添加令牌密钥 2.1:5

addTokenKey 2.2:6

另一个请求结果

添加令牌密钥 2.1:5

addTokenKey 2.2:6

如何避免这种情况的发生? 锁定还是..?


参考:https://github.com/zerkalica/Semaphore

我使用这个库尝试进行锁定和释放,代码如下:

    function addTokenKey($token)
    {
       $adapter   = new MemcachedAdapter($this->memcache);
       $semaphore = new SemaphoreManager($adapter); 

       $ttl = 60; // Time in seconds, used, if script dies and release never called.
       $handle = $semaphore->acquire('addTokenKey_lock_key', $ttl);
       $allTokens = $this->memcache->get("AllTokens");
        if($allTokens == false)
        {
            //array_push($allTokens,$token);

            $array = array();
            array_push($array,$token);
            $this->memcache->set("AllTokens",$array);
            echo "addTokenKey 1.2:".count($array)."<br>";
        }
        else{
            echo "addTokenKey 2.1:".count($allTokens)."<br>";
            array_push($allTokens,$token);
            $result = $this->memcache->set("AllTokens",$allTokens);     
            echo "addTokenKey 2.2:".count($allTokens)." ".$result."<br>";
        }
        $semaphore->release($handle);
     }

但我总是得到两个错误

致命错误:未捕获的异常"ErrorException",在第 50 行的/xxxxxx/Server/lib/Semaphore/SemaphoreManager.php中带有消息"无法获取millwright_semaphoreaddTokenKey_lock_key的锁定">

致命错误:未捕获的异常"LogicException",在第 65 行的/xxxxxxxx/Server/lib/Semaphore/SemaphoreManager.php中带有消息"首先调用 ::acquire('millwright_semaphoremillwright_semaphoreaddTokenKey_lock_key'(

我已经在信号量管理器中修复了此错误.php通过删除"$this->前缀"代码

但仍然存在未命中数组计数问题。

我修改下面的一些代码来尝试,

我发送了100个请求,最后所有代币数量只有50,

其他人将显示"无法设置">

    function addTokenKey($token)
    {            
        // initialize lock
        $lock = FALSE;
        // initialize configurable parameters
        $tries = 0;
        $max_tries = 1000;
        $lock_ttl = 10;
        $allTokens = $this->memcache->get("AllTokens");
        while($lock === FALSE && $tries < $max_tries ) {
            if( $allTokens == false ) {            
                $allTokens = array();
                array_push($allTokens,$token);
                $this->memcache->set("AllTokens",$allTokens);
                echo "addTokenKey 1.2:".count($allTokens)."<br>"; 
                return;
            } 
            $count = count($allTokens) ;
            // add() will return false if someone raced us for this lock
            // ALWAYS USE add() FOR CUSTOM LOCKS
            $lock = $this->memcache->add("lock_".$count, 1, $lock_ttl);
            $tries++;
            usleep(100*($tries%($max_tries/10))); // exponential backoff style of sleep        
        }
        if($lock === FALSE && $tries >= $max_tries) {    
            print("Unable to set");
        } else {        
            echo "addTokenKey 2.1:".count($allTokens)."<br>";
            array_push($allTokens,$token);
            $this->memcache->set("AllTokens",$allTokens);   
            echo "addTokenKey 2.2:".count($allTokens)."<br>";        
        }            
     }

最后我用memcached getAllKeys功能来解决这个问题,不要DIY来记录allTokens。但这个函数只能在linux memcached中使用,Windows memcache不支持getAllKeys

在正常的Senario中,此问题将不可见,但只有在有n个并发请求时才会产生问题。这是因为 memecache 更新与其正常的 get/set 不是原子的。始终使用 memcached 递增/递减来确保为请求中将有并发的 senario 设置整数值键的原子性。由于 memcached increment(( 本身是原子的,我们不需要放置任何锁定机制。是的,但是为了获得任何其他竞争条件的原子性,您必须应用自定义锁定等以确保并发请求的原子性。

尝试如下并检查它:

$mem = new Memcache;
$mem->addServer("127.0.0.1", 11211);
function incrementUserVisits($userIdFromRequest) {
    global $mem;    
    $key = "visit_".$userIdFromRequest;
    $count = $mem->increment($key, 1);
    if( $count === FALSE ) {
        $count = $mem->add($key, 1, 0, 0);
        if($count === FALSE) {          
            $count = $mem->increment($key, 1);
            if($count === FALSE) {              
                return FALSE;
            }
            else {              
                return TRUE;
           }
       }
       else {           
           return TRUE;
       }
   }
   else {       
      return TRUE;
   }
}
incrementUserVisits($userIdFromRequest);

您可以尝试以下代码(经过一些研究,我设法组合/构建了(,但我甚至没有测试语法错误,但觉得它将帮助您实现自定义锁以处理竞争条件。

$mem = new Memcache;
$mem->addServer("127.0.0.1", 11211);
function addTokenKey($token) {
    global $mem;
    // initialize lock
    $lock = FALSE;
    // initialize configurable parameters
    $tries = 0;
    $max_tries = 1000;
    $lock_ttl = 10;
    $allTokens = $mem->get("AllTokens");
    while($lock === FALSE && $tries < $max_tries ) {
        if( gettype($allTokens) == "boolean" ) {            
            $allTokens = array();
            array_push($allTokens,$token);
            $mem->set("AllTokens",$allTokens);
            echo "addTokenKey 1.2:".count($allTokens)."<br>";                        
        } 
        $count = count($allTokens) ;
        // add() will return false if someone raced us for this lock
        // ALWAYS USE add() FOR CUSTOM LOCKS
        $lock = $mem->add("lock_".$count, 1, 0, $lock_ttl);
        $tries++;
        usleep(100*($tries%($max_tries/10))); // exponential backoff style of sleep        
    }
    if($lock === FALSE && $tries >= $max_tries) {    
        print("Unable to set");
    } else {        
        echo "addTokenKey 2.1:".count($allTokens)."<br>";
        array_push($allTokens,$token);
        $mem->set("AllTokens",$allTokens, 0, 0);   
        echo "addTokenKey 2.2:".count($allTokens)."<br>";        
    }
}
addTokenKey('XXX111');

对任何错误表示歉意,但我认为你可以玩它,可以实现你想要的。