Php

PHP - Memcached - Libmemcached - 處理記憶體伺服器中斷

  • August 26, 2014

addServer我正在努力確保我們的應用程序在記憶體完全中斷的情況下正常降級,這是極不可能的,因為通過 PHP 的 memcached api 呼叫,我們至少有 3 個記憶體節點要添加到記憶體池中。但是,有可能單個節點出現故障,我需要確保 memcached api 正確處理這種情況。

這是我目前的 cache.yml 配置

port: 11211
<?php echo Hobis_Api_Cache::TYPE_VOLATILE; ?>:
 options:
   - <?php echo Memcached::OPT_CONNECT_TIMEOUT; ?>: 25<?php echo PHP_EOL; ?>
   #- <?php echo Memcached::OPT_DISTRIBUTION; ?>: <?php echo Memcached::DISTRIBUTION_CONSISTENT; ?><?php echo PHP_EOL; ?>
   - <?php echo Memcached::OPT_LIBKETAMA_COMPATIBLE; ?>: true<?php echo PHP_EOL; ?>
   - <?php echo Memcached::OPT_NO_BLOCK; ?>: true<?php echo PHP_EOL; ?>
   #- <?php echo Memcached::OPT_POLL_TIMEOUT; ?>: 100<?php echo PHP_EOL; ?>
   #- <?php echo Memcached::OPT_RECV_TIMEOUT; ?>: 10000<?php echo PHP_EOL; ?>
   - <?php echo Memcached::OPT_REMOVE_FAILED_SERVERS; ?>: true<?php echo PHP_EOL; ?>
   - <?php echo Memcached::OPT_RETRY_TIMEOUT; ?>: 1<?php echo PHP_EOL; ?>
   #- <?php echo Memcached::OPT_SEND_TIMEOUT; ?>: 10000<?php echo PHP_EOL; ?>
   - <?php echo Memcached::OPT_SERIALIZER; ?>: <?php echo Memcached::SERIALIZER_IGBINARY; ?><?php echo PHP_EOL; ?>
   #- <?php echo Memcached::OPT_SERVER_FAILURE_LIMIT; ?>: 1<?php echo PHP_EOL; ?>
   - <?php echo Memcached::OPT_TCP_NODELAY; ?>: true<?php echo PHP_EOL; ?>
 servers:
   - vcache-1
   - vcache-2
   - vcache-3
<?php echo Hobis_Api_Cache::TYPE_PERSISTENT; ?>:
 servers:
   - pcache-1

根據一些研究(這里這裡),如果是池的一部分,memcached api 可以優雅地處理單個節點中斷。但是就我而言,我無法在測試期間編寫特定的密鑰。相反,我得到一個 resultCode 為 35 的“無法寫入”錯誤,根據評論,它是MEMCACHED_SERVER_MARKED_DEAD.

將伺服器標記為已死確實是我所期望的,因為我停止了 vcache-2/3 並且只有 vcache-1 正在執行,但是,使用該OPT_LIBKETAMA_COMPATIBLE選項,我的印像是 memcached api 是將密鑰寫入另一個池中的伺服器。有了這個OPT_REMOVE_FAILED_SERVERS選項,我不應該看到標記為死的結果程式碼,因為伺服器應該從池中刪除。

有什麼建議麼?

最後,我有一個可行的解決方案,並與其他人分享,以防他們遇到同樣的問題。我的解決方案的基礎來自這篇文章,我一看到它就明白了$testInstance$realInstance使用測試實例來確定哪些伺服器可用,然後將已知好的伺服器添加到真實實例中。你可能會問自己,為什麼不對addServers同一個實例呼叫兩次,答案是什麼?你不能。

如果您嘗試addServers對同一個實例多次呼叫,您應該會看到意外的行為,對我來說這是一個錯誤的網關,原因是:“上游過早地關閉了連接,同時從上游讀取響應標頭”。所以有一些內部機制導致 PHP 失敗,雖然我不知道具體原因,因為錯誤日誌中沒有顯示錯誤。

我沒有像作者那樣進行顯式連接呼叫,而是選擇使用可用getStats()呼叫並檢查 pid。

工作片段:

$cacheReal = new Memcached;
$cacheTest = new Memcached;

if (count($cacheTest->getServerList()) < 1) {

   $knownGoodServers   = array();
   $serversToAdd       = array();

   foreach ($servers as $server) {             
       $serversToAdd[] = array($server, $port);
   }

   $cacheTest->addServers($serversToAdd);

   foreach ($cacheTest->getStats() as $server => $stats) {

       // Test if server is actually available
       if ((false === Hobis_Api_Array_Package::populatedKey('pid', $stats)) ||
           ($stats['pid'] < 0)) {
           continue;
       }

       $knownGoodServers[] = $server;
   }

   // It is possible that entire cache pool took a dump
   if (true === Hobis_Api_Array_Package::populated($knownGoodServers)) {

       $serversToAdd = array();

       foreach ($knownGoodServers as $server) {

           list($host, $port) = array_map('trim', explode(':', $server));

           $serversToAdd[] = array($host, (int) $port);
       }

       if (true === Hobis_Api_Array_Package::populated($serversToAdd)) {
           $cacheReal->addServers($serversToAdd);
       }
   }
}

在我的測試中,我有多個記憶體節點都在為測試 1 執行,然後對於後續測試,我關閉並打開了一些記憶體節點,最後全部關閉(以測試降級)。唯一明顯的區別是,在關閉節點或添加節點(通過守護程序重啟)後,我登錄的會話將被註銷,這是有道理的,因為記憶體的數據在預期的伺服器上不再可用。但是,重新登錄後的後續請求在登錄時表現出預期的行為,因為會話數據已寫入請求時可用的記憶體節點。

引用自:https://serverfault.com/questions/623949