Memcached一致性散列不能与4个服务器中的3个一起使用

Dan*_* W. 11 php memcached caching consistent-hashing libketama

故事

我有3个memcached服务器运行,我关闭其中一个,以调查PHP-memcached在服务器无法访问时的行为方式.

我在PHP中定义了4个服务器,1个用于模拟大部分离线的服务器(备用服务器).当我关闭1台服务器(=> 2仍在线)时,第三台->get()给我一个结果.

当我再关闭一台服务器(=> 1仍在线)时,它将找不到推送到最后一台服务器的对象.

样本输出

首次运行,4台服务器中的3台:

Entity not found in cache on 1st try: NOT FOUND
Entity not found in cache on 2nd try: NOT FOUND
Entity not found in cache on 3rd try: NOT FOUND
Entity not found in cache on 4th try: NOT FOUND
Run Code Online (Sandbox Code Playgroud)

第二次运行,4台服务器中的3台:

Entity found in Cache: SUCCESS
Run Code Online (Sandbox Code Playgroud)

第三次运行,4台服务器中的2台:

Entity not found in cache on 1st try: CONNECTION FAILURE
Entity not found in cache on 2nd try: SERVER IS MARKED DEAD
Entity not found in cache on 3rd try: NOT FOUND
Entity not found in cache on 4th try: NOT FOUND
Run Code Online (Sandbox Code Playgroud)

第四次运行,4台服务器中的1台:

Entity not found in cache on 1st try: CONNECTION FAILURE
Entity not found in cache on 2nd try: SERVER IS MARKED DEAD
Entity not found in cache on 3rd try: CONNECTION FAILURE
Entity not found in cache on 4th try: SERVER IS MARKED DEAD
Run Code Online (Sandbox Code Playgroud)

虽然有一台服务器保持在线状态,但每次在缓存中找不到任何服务器时,我都会将对象推送到memcached,因此无法再找到该密钥.

我认为它也应该只用一个服务器.

你能解释一下这种行为吗?

即使我关闭了20个服务器中的19个,看起来也不可能实现安全的东西.

Sidequestion:libketama不再维护了,使用它还是好的吗?lib背后的逻辑非常好,也用于清漆缓存服务器.

附录

我的剧本:

<?php
require_once 'CachableEntity.php';
require_once 'TestEntity.php';

echo PHP_EOL;

$cache = new Memcached();
$cache->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
$cache->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$cache->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 1);
$cache->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, true);
$cache->setOption(Memcached::OPT_AUTO_EJECT_HOSTS, true);

$cache->setOption(Memcached::OPT_TCP_NODELAY, true);
//$cache->setOption(Memcached::OPT_RETRY_TIMEOUT, 10);

$cache->addServers([
    ['localhost', '11212'],
    ['localhost', '11213'],
    ['localhost', '11214'],
    ['localhost', '11215'], // always offline
]);


$entityId = '/test/test/article_123456789.test';

$entity = new TestEntity($entityId);

$found = false;

$cacheKey = $entity->getCacheKey();

$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
    echo 'Entity not found in cache on 1st try: ' . $cache->getResultMessage(), PHP_EOL;

    $cacheResult = $cache->get($cacheKey);
    if (empty($cacheResult)) {
        echo 'Entity not found in cache on 2nd try: ' . $cache->getResultMessage(), PHP_EOL;

        $cacheResult = $cache->get($cacheKey);
        if (empty($cacheResult)) {
            echo 'Entity not found in cache on 3rd try: ' . $cache->getResultMessage(), PHP_EOL;

            $cacheResult = $cache->get($cacheKey);
            if (empty($cacheResult)) {
                echo 'Entity not found in cache on 4th try: ' . $cache->getResultMessage(), PHP_EOL;

                $entity
                    ->setTitle('TEST')
                    ->setText('Hellow w0rld. Lorem Orem Rem Em M IpsuM')
                    ->setUrl('http://www.google.com/content-123456789.html');

                $cache->set($cacheKey, $entity->serialize(), 120);
            }
        }
        else { $found = true; }
    }
    else { $found = true; }
}
else { $found = true; }


if ($found === true) {
    echo 'Entity found in Cache: ' . $cache->getResultMessage(), PHP_EOL;
    $entity->unserialize($cacheResult);
    echo 'Title: ' . $entity->getTitle(), PHP_EOL;
}

echo PHP_EOL;
Run Code Online (Sandbox Code Playgroud)

Ada*_*dam 5

  • 您遇到的行为是一致的.当服务器不可用时,它首先标记为失败,然后标记为已死.

问题是,如果您Memcached::OPT_SERVER_FAILURE_LIMIT将此值设置为2,那么显然它只会是连贯的.这可以解释为什么每个无法访问的服务器有两个错误行(CONNECTION FAILURE,SERVER IS MARKED AS DEAD)

这似乎与超时有关.usleep()使用匹配OPT_RETRY_TIMEOUT值添加失败后,将允许从列表中删除服务器(请参阅以下错误注释)

  • 该值不会复制到下一个服务器,因为只分发了密钥.

  • 请注意,OPT_LIBKETAMA_COMPATIBLE不使用libketama,但仅重现相同的算法,这意味着如果libketama不再处于活动状态并不重要,这是PHP文档中的推荐配置:

如果要使用一致性散列,强烈建议启用此选项,并且在将来的版本中可能默认启用它.

编辑: 在我对你的帖子的理解中,消息"在Cache:SUCCESS中找到的实体"只出现在第二次运行(1个服务器离线),因为上一个命令没有变化,托管这个密钥的服务器仍然可用(所以memcached从密钥中考虑该值存储在第一,第二或第三服务器上).我们称这些服务器为John,George,Ringo和Paul.

在第三次运行中,在开始时,memcached从密钥中推断出四个服务器中的哪一个拥有该值(例如John).它在放弃之前要求约翰两次,因为它现在已经关闭了.然后它的算法只考虑3个服务器(不知道Paul已经死了)并推断出George应该包含该值.

乔治回答了两次,因为它不包含值,然后存储它.

但在第四轮比赛中,约翰,乔治和保罗都不在了.Memcached两次尝试John,然后两次尝试George.然后它存储在林戈.

这里的问题是不同的运行之间不存储不可用的服务器,并且在同一运行中,您必须在服务器被删除之前询问两次.