PHP - Memcached - Libmemcached - Lidar com a indisponibilidade do servidor de cache

1

Estou trabalhando para garantir que o nosso aplicativo seja degradavelmente correto em caso de interrupção completa do cache, o que é altamente improvável, pois temos pelo menos 3 nós de cache para adicionar ao pool de cache, por meio do memcached addServer do PHP chamada de api. No entanto, é possível que um único nó possa falhar e eu preciso garantir que a API do memcached manipule corretamente esse cenário.

Aqui está minha configuração atual de 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

Com base em algumas pesquisas ( aqui e aqui ), é possível que a API memcached manipule normalmente uma interrupção de um único nó, se fizer parte de um pool. No entanto, no meu caso, não consigo escrever chaves específicas durante o teste. Em vez disso, recebo um erro "não é possível gravar" com um resultCode de 35, que de acordo com os comentários , é MEMCACHED_SERVER_MARKED_DEAD .

Ter um servidor marcado como morto é de fato o que eu esperaria, pois parei o vcache-2/3 e somente o vcache-1 está rodando, no entanto, com a opção OPT_LIBKETAMA_COMPATIBLE , tenho a impressão de que a API do memcached é para gravar a chave em outro servidor no pool. E que, com a opção OPT_REMOVE_FAILED_SERVERS , eu não deveria estar vendo os códigos de resultado mortos marcados, porque o servidor deve ser removido do pool.

Alguma sugestão?

    
por Mike Purcell 26.08.2014 / 01:35

1 resposta

0

Por fim, tenho uma solução em funcionamento e compartilho com outras pessoas, caso elas encontrem o mesmo sitch. A base para a minha solução veio desse post , assim que eu vi o $testInstance e $realInstance me ocorreu, use a instância de teste para determinar quais servidores estão disponíveis, então adicione bons servidores conhecidos ao real instância. Você pode estar se perguntando, por que não chamar apenas addServers duas vezes na mesma instância, a resposta? Você não pode.

Se você tentar chamar addServers mais de uma vez na mesma instância, verá um comportamento inesperado; para mim, foi um gateway incorreto devido a: "conexão encerrada antecipadamente fechada durante a leitura do cabeçalho de resposta do upstream". Então, houve algum mecanismo interno fazendo com que o PHP falhasse, embora eu não saiba por que especificamente, já que nenhum erro apareceu no log de erros.

Em vez de fazer chamadas de conexão explícitas como o autor fez, optei por usar a chamada getStats() disponível e verificar se há um pid.

Fragmento de trabalho:

$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);
        }
    }
}

No meu teste, eu tinha vários nós de cache que estavam todos em execução para o teste 1 e, em testes subsequentes, eu desliguei alguns nós de cache e, finalmente, desliguei todos (para testar a degradação). A única diferença notável foi que, depois de desativar um nó ou adicionar um nó (por meio do reinício do daemon), minha sessão conectada seria desconectada, o que faz sentido, pois os dados armazenados em cache não estão mais disponíveis no servidor esperado. No entanto, as solicitações subseqüentes após o logon no comportamento esperado demonstrado durante o login, porque os dados da sessão foram gravados em nós de cache disponíveis no momento da solicitação.

    
por 27.08.2014 / 00:40