Назад | Перейти на главную страницу

PHP - Memcached - Libmemcached - Обработка сбоев кеш-сервера

Я работаю над тем, чтобы наше приложение плавно деградировало в случае полного отключения кеша, что маловероятно, поскольку у нас есть минимум 3 узла кеша для добавления в пул кеша с помощью PHP memcached. addServer вызов api. Однако возможно, что один узел может выйти из строя, и мне нужно убедиться, что 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

На основании некоторых исследований (Вот и Вот), api memcached может корректно обрабатывать отказ одного узла, если он является частью пула. Однако в моем случае я не могу писать конкретные ключи во время тестирования. Вместо этого я получаю сообщение об ошибке «Невозможно записать» с кодом результата 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, затем для последующих тестов я выключал и включал некоторые узлы кеша, а затем, наконец, все выключал (для проверки деградации). Единственная заметная разница заключалась в том, что после отключения узла или добавления узла обратно (через перезапуск демона) мой сеанс, вошедший в систему, будет отключен, что имеет смысл, поскольку кэшированные данные больше не доступны на ожидаемом сервере. Однако последующие запросы после повторного входа в систему продемонстрировали ожидаемое поведение во время входа в систему, поскольку данные сеанса были записаны в узлы кеша, доступные на момент запроса.