我使用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>";
}
}
我发送多个请求同时调用此函数
但有时我会得到相同的结果,例如:
请求结果
addTokenKey 2.1:5
addTokenKey 2.2:6
另一个请求结果
addTokenKey 2.1:5
addTokenKey 2.2:6
如何避免这种情况发生?锁或..?
请参阅:https://github.com/zerkalica/Semaphore
我使用这个库来尝试锁定&amp;发布,代码如下:
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);
}
但我总是有两个错误
致命错误:未捕获的异常&#39; ErrorException&#39;有消息&#39;无法获得millwright_semaphoreaddTokenKey_lock_key&#39;在
致命错误:未捕获的异常&#39; LogicException&#39;使用消息&#39; Call :: acquire(&#39; millwright_semaphoremillwright_semaphoreaddTokenKey_lock_key&#39;)first&#39;在第65行的/xxxxxxx/Server/lib/Semaphore/SemaphoreManager.php中
我已经通过删除&#34; $ this-&gt;前缀修复了SemaphoreManager.php中的此错误。&#34;代码
我修改下面的代码尝试,
我发送了100个请求,最后allTokens号码只有50个,
其他人将显示&#34;无法设置&#34;
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
答案 0 :(得分:1)
在正常情况下,此问题将不可见,但只有在存在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');
对任何错误道歉但我认为你可以玩它并且可以实现你想要的。