[英]php memcached set function lock
I use php memcached to implement token the code is below: 我使用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>";
}
}
I send mulitple request to call this function at the same time 我发送多个请求以同时调用此函数
but sometime I get the same result,Ex: 但有时我得到相同的结果,例如:
request result 请求结果
addTokenKey 2.1:5 addTokenKey 2.1:5
addTokenKey 2.2:6 addTokenKey 2.2:6
another request result 另一个请求结果
addTokenKey 2.1:5 addTokenKey 2.1:5
addTokenKey 2.2:6 addTokenKey 2.2:6
How to avoid this case happen? 如何避免这种情况发生? lock or ..?
锁还是..?
refer to: https://github.com/zerkalica/Semaphore 请参阅: https : //github.com/zerkalica/Semaphore
I use this library to try to do lock & release,the code is below: 我使用这个库尝试进行锁定和释放,代码如下:
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);
}
but I always got two error 但是我总是有两个错误
Fatal error: Uncaught exception 'ErrorException' with message 'Can't acquire lock for millwright_semaphoreaddTokenKey_lock_key' in /xxxxxxx/Server/lib/Semaphore/SemaphoreManager.php on line 50 致命错误:在第50行的/xxxxxxx/Server/lib/Semaphore/SemaphoreManager.php中,消息“无法获取millwright_semaphoreaddTokenKey_lock_key的锁”的未捕获异常'ErrorException'
Fatal error: Uncaught exception 'LogicException' with message 'Call ::acquire('millwright_semaphoremillwright_semaphoreaddTokenKey_lock_key') first' in /xxxxxxx/Server/lib/Semaphore/SemaphoreManager.php on line 65 致命错误:/xxxxxxx/Server/lib/Semaphore/SemaphoreManager.php中的消息'Call :: acquire('millwright_semaphoremillwright_semaphoreaddTokenKey_lock_key')first'未捕获的异常'LogicException'
I already fix this error in SemaphoreManager.php by removing "$this->prefix ." 我已经通过删除“ $ this-> prefix”解决了SemaphoreManager.php中的错误。 code
码
I modify some code below to try, 我修改下面的代码以尝试,
I send 100 request,finally allTokens number is only 50, 我发送100个请求,最终所有令牌数只有50个,
others will show "Unable to set" 其他人将显示“无法设置”
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>";
}
}
finally I use memcached getAllKeys function to fix the problem,don't DIY to record allTokens but this function only can use in linux memcached, windows memcache don't support getAllKeys 最后我使用memcached的getAllKeys函数解决了这个问题,不要自己动手记录allTokens,但是这个函数只能在linux的memcached中使用,而Windows的memcache不支持getAllKeys
In normal senario, this issue will not be visible but it will only be creating problem when there will be n number of concurrent requests. 在普通的senario中,此问题将不可见,但仅在并发请求数为n时才会产生问题。 And that is because the memecache update is not atomic with its normal get/set.
这是因为memecache更新不是正常的get / set原子操作。 Always use memcached increment/decrement for insuring the atomicity for setting integer valued keys for senario where there will be concurrency in the requests.
始终使用memcached增量/减量来确保为senario设置整数值键的原子性,其中请求中将存在并发性。 Since memcached increment() is atomic by itself, we need not put any locking mechanism.
由于memcached gain()本身是原子的,因此我们不需要放置任何锁定机制。 Yes , but for acheiveing the atomicity for any other race conditions , you will have to apply the custom locking etc to insure the atomicity for concurrent requests.
是的,但是要实现其他竞争条件的原子性,您将必须应用自定义锁定等以确保并发请求的原子性。
Try like below and check it: 尝试如下检查并检查:
$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);
You can try the below code ( i have managed to combine/build after a bit of research) but i have not tested it even for a syntax error but feels that it will help you in achieving the custom lock to handle the race conditions. 您可以尝试下面的代码(经过一些研究,我已经设法组合/构建),但是即使是语法错误,我也都没有对其进行测试,但是它会帮助您实现处理比赛条件的自定义锁定。
$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');
Apology for any error but i think you can play with it and can achieve what you are looking for. 道歉的任何错误,但我认为您可以使用它,并可以实现您想要的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.