php - Memcached 一致性哈希不适用于 4 台服务器中的 3 台宕机

标签 php caching memcached consistent-hashing libketama

故事

我有 3 个正在运行的内存缓存服务器,我关闭其中一个或另一个以调查 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

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

Entity found in Cache: SUCCESS

第三次运行,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

第四次运行,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

尽管只有一台服务器在线,而且每次我都将我的对象推送到 memcached,但它在缓存中找不到任何对象时,它无法再找到 key 。

我认为它也应该只剩下一台服务器。

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

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

附带问题:libketama 真的不再维护了,它还好用吗? lib 背后的逻辑相当不错,也用于 varnish 缓存服务器。

附录

我的脚本:

<?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;

最佳答案

  • 您遇到的行为是一致的。当服务器不可用时,它首先被标记为故障,然后被标记为死机。

问题是,显然只有当您将 Memcached::OPT_SERVER_FAILURE_LIMIT 值设置为 2 而将其设置为 1 时,它才会连贯。这可以解释为什么每个错误行都有两条无法访问的服务器(CONNECTION FAILURESERVER IS MARKED AS DEAD)

这似乎与超时有关。在具有匹配的 OPT_RETRY_TIMEOUT 值的失败后添加 usleep() 将使服务器能够从列表中删除(参见 following bug comment )

  • 值不会复制到下一个服务器,因为只分发 key 。

  • 请注意,OPT_LIBKETAMA_COMPATIBLE 不使用 libketama,而只是重现相同的算法,这意味着如果 libketama 不再处于事件状态并不重要,而这是推荐的配置 in PHP documentation :

It is highly recommended to enable this option if you want to use consistent hashing, and it may be enabled by default in future releases.

编辑: 根据我对您的帖子的理解,消息“在缓存中找到的实体:成功”仅出现在第二次运行时(1 台服务器离线),因为与上一个命令相比没有变化并且托管此 key 的服务器仍然可用(因此 memcached 考虑从值存储在第一台、第二台或第三台服务器上的键)。我们称这些服务器为 John、George、Ringo 和 Paul。

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

George 回答两次它不包含该值然后存储它。

但是在第四轮比赛中,约翰、乔治和保罗都出局了。 Memcached 尝试 John 两次,然后尝试 George 两次。然后存储在 Ringo 中。

这里的问题是不可用的服务器在不同的运行之间没有被记住,并且在同一次运行中你必须在它被删除之前询问服务器两次。

关于php - Memcached 一致性哈希不适用于 4 台服务器中的 3 台宕机,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47910288/

相关文章:

php - 阻止 Safari 缓存热门站点

memcached - JMeter - 使用 beanshell 通过 telnet 执行命令

caching - Cache 和 Translation LookAside Buffer [TLB] 的区别

memcached - Enyim Memcached 客户端不适用于传递的过期参数

laravel - 为什么 laravel ubuntu 服务器占用这么多硬盘空间

php - 如果使用 GET 而不是 POST,则旧的 PayPal 结帐页面

javascript - Highcharts 金字塔图表使用 JSON、PHP 显示无数据

Php/MySQL if 语句根据所选值返回信息

php - 在文件夹内创建图像会导致创建名称为 folder/image.jpg 的图像

iphone - 在 iOS 5 应用程序中将文件保存在何处?