Согласованное хеширование Memcached не работает с 3 из 4 серверов
История
У меня есть 3 сервера memcached, на которых я отключаю один или другой, чтобы исследовать, как PHP-memcached ведет себя на сервере, который недоступен.
Я определил 4 сервера в PHP, 1 для имитации сервера, который в основном находится в автономном режиме (запасной сервер). Когда я выключаю 1 сервер (=> 2 все еще в сети), третий ->get()
дает мне результат.
Когда я выключаю еще один сервер (=> 1 все еще в сети), он не найдет объекты, отправленные на этот последний сервер.
Образец вывода
Первый запуск, 3 из 4 серверов:
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
Второй запуск, 3 из 4 серверов:
Entity found in Cache: SUCCESS
Третий запуск, 2 из 4 серверов:
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
Четвертый запуск, 1 из 4 серверов:
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 каждый раз, когда он не находит ничего в кэше, он больше не может найти ключ.
Я думаю, что это также должно работать только с одним оставленным сервером.
Можете ли вы объяснить это поведение мне?
Похоже, что невозможно реализовать что-то безопасное, даже когда я отключил 19 из 20 серверов.
Дополнительный вопрос: либкетама на самом деле больше не поддерживается, все же хорошо ли ее использовать? Логика, лежащая в основе 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;
2 ответа
- Поведение, которое вы испытываете, является последовательным. Когда сервер недоступен, он сначала помечается как сбой, а затем помечается как мертвый.
Проблема в том, что, по-видимому, это было бы только согласованным, если бы вы установили Memcached::OPT_SERVER_FAILURE_LIMIT
в то время как вы установили это значение равным 1. Это объяснило бы, почему у вас есть две строки ошибок на недостижимый сервер (CONNECTION FAILURE
, SERVER IS MARKED AS DEAD
)
Похоже, это связано с тайм-аутом. Добавление usleep()
после сбоя с соответствием OPT_RETRY_TIMEOUT
значение позволит удалить сервер из списка (см. следующий комментарий об ошибке)
Значение не реплицируется на следующий сервер, поскольку распространяются только ключи.
Обратите внимание, что
OPT_LIBKETAMA_COMPATIBLE
не использует libketama, но воспроизводит только тот же алгоритм, что означает, что не имеет значения, если libketama больше не активна, хотя это рекомендуемая конфигурация в документации PHP:
Настоятельно рекомендуется включить эту опцию, если вы хотите использовать согласованное хеширование, и она может быть включена по умолчанию в будущих выпусках.
РЕДАКТИРОВАТЬ: В моем понимании вашего сообщения, сообщение "Объект найден в Cache: SUCCESS" появляется только при втором запуске (1 сервер отключен), потому что нет изменений по сравнению с предыдущей командой, и сервер, на котором размещен этот ключ, все еще доступен (поэтому memcached считайте по ключу, что значение хранится на 1-м, 2-м или 3-м сервере). Давайте назовем эти серверы Джоном, Джорджем, Ринго и Полом.
При третьем запуске, при запуске, memcached выводит из ключа, какой из четырех серверов владеет значением (например, Джон). Он дважды спрашивает Джона, прежде чем сдаться, потому что сейчас он выключен. Его алгоритм тогда учитывает только 3 сервера (не зная, что Пол уже мертв) и приходит к выводу, что Джордж должен содержать значение.
Джордж дважды отвечает, что оно не содержит значения, а затем сохраняет его.
Но на четвертой дистанции Джон, Джордж и Пол выключены. Memcached дважды пробует Джона, а затем дважды. Затем он хранит в Ринго.
Проблема здесь в том, что недоступные серверы не запоминаются между различными запусками, и что в течение одного запуска вы должны дважды запросить сервер перед его удалением.
избыточность
Начиная с Memcached 3.0.0, существует конфигурация избыточности.
- Это можно сделать в файле конфигурации расширения.
/etc/php/7.0/mods-available/memcached.ini (может отличаться в разных операционных системах)
memcache.redundancy=2
- с ini_set('memcache.redundancy', 2)
Этот параметр на самом деле не задокументирован, вы можете заменить "2" на количество серверов, это добавит небольшие накладные расходы с дополнительными записями.
Потеря 19/20 серверов
Из-за избыточности вы можете потерять некоторые серверы и сохранить "успех чтения".
Заметки:
- потеря 95% пула серверов вызовет нагрузку на оставшиеся.
- Кэш-серверы созданы для производительности, "большое количество серверов может замедлить работу клиента с нескольких сторон"
libketama
Репозиторий Github не получал никаких коммитов с 2014 года. Libketama ищет нового сопровождающего https://github.com/RJ/ketama