[英]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>";
}
}
我發送多個請求以同時調用此函數
但有時我得到相同的結果,例如:
請求結果
addTokenKey 2.1:5
addTokenKey 2.2:6
另一個請求結果
addTokenKey 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);
}
但是我總是有兩個錯誤
致命錯誤:在第行的/xxxxxxx/Server/lib/Semaphore/SemaphoreManager.php中,消息“無法獲取millwright_semaphoreaddTokenKey_lock_key的鎖”的未捕獲異常'ErrorException'
致命錯誤:/xxxxxxx/Server/lib/Semaphore/SemaphoreManager.php中的消息'Call :: acquire('millwright_semaphoremillwright_semaphoreaddTokenKey_lock_key')first'未捕獲的異常'LogicException'
我已經通過刪除“ $ this-> prefix”解決了SemaphoreManager.php中的錯誤。 碼
我修改下面的代碼以嘗試,
我發送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函數解決了這個問題,不要自己動手記錄allTokens,但是這個函數只能在linux的memcached中使用,而Windows的memcache不支持getAllKeys
在普通的senario中,此問題將不可見,但僅在並發請求數為n時才會產生問題。 這是因為memecache更新不是正常的get / set原子操作。 始終使用memcached增量/減量來確保為senario設置整數值鍵的原子性,其中請求中將存在並發性。 由於memcached gain()本身是原子的,因此我們不需要放置任何鎖定機制。 是的,但是要實現其他競爭條件的原子性,您將必須應用自定義鎖定等以確保並發請求的原子性。
嘗試如下檢查並檢查:
$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');
道歉的任何錯誤,但我認為您可以使用它,並可以實現您想要的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.