PHP - Memcached - Libmemcached - 处理缓存服务器中断

PHP - Memcached - Libmemcached - 处理缓存服务器中断

我正在努力确保我们的应用程序在缓存完全中断的情况下能够正常降级,这种情况极不可能发生,因为我们至少有 3 个缓存节点可以通过 PHP 的 memcached addServerapi 调用添加到缓存池中。但是,单个节点可能会发生故障,我需要确保 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,我不应该看到标记为已死的结果代码,因为服务器应该从池中移除。

有什么建议么?

答案1

最后,我找到了一个可行的解决方案,并与其他人分享,以防他们遇到同样的情况。我的解决方案的基础来自于此邮政,我一看到就$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,然后对于后续测试,我关闭和打开了一些缓存节点,最后全部关闭(以测试降级)。唯一明显的区别是,在关闭一个节点或重新添加一个节点(通过守护程序重新启动)后,我的登录会话将被注销,这是有道理的,因为缓存数据在预期服务器上不再可用。然而,重新登录后的后续请求表现出登录时的预期行为,因为会话数据已写入请求时可用的缓存节点。

相关内容