Почему куратор не выздоравливает, когда смотритель зоопарка снова в сети?

#java #apache-zookeeper #apache-curator

Вопрос:

У меня есть CuratorFramework клиент (v5.1.0), работающий на сервере Zookeeper (v3.7.0). Если сервер Zookeeper выключен, пока клиент подключен к нему, я могу видеть состояния соединения (с a ConnectionStateListener ) SUSPENDED , а затем LOST и больше ничего, даже когда сервер возвращается в сеть.

Это похоже на очень стандартный вариант использования, и я, должно быть, упускаю что-то глупое, но я никогда не смогу заставить клиента подключиться снова, как только сервер будет подключен.

Я провел некоторый поиск в Google и не нашел ничего полезного о том, как справиться с восстановлением после ПОТЕРЯННОГО состояния.

У меня есть автономный пример того, что я делаю с примером кода в классе CuratorRecoveryTest (выполняется в среде IDE, а не в maven). Его мясо (извлечено из тестового класса):

 // setup the server and client
server = new TestingServer();

client = newClient(server.getConnectString(), 60000, 15000, new RetryNTimes(1, 250));
client.start();
client.blockUntilConnected();
            
// add the listener
final var stateListener = new StateListener();
stateListener.stateChanged(client, CONNECTED);

// register the listener
client.getConnectionStateListenable().addListener(stateListener);

// verify connection
assertTrue(client.getZookeeperClient().isConnected());

// let things settle
nap(3, "initial settling");

// stop zk
stopServer();
log.info(">>>>>>>>>> STOPPED ZK SERVER");

// let it bake
nap(3, "letting things bake");

// ensure disconnected
assertFalse(client.getZookeeperClient().isConnected());

nap(3, "disconnecting");

// start zk
server.start();
log.info(">>>>>>>>>> STARTED ZK SERVER");

await().atMost(5, MINUTES).until(() -> stateListener.getCurrentState() == CONNECTED || stateListener.getCurrentState() == RECONNECTED);

// NOTE: it never gets here - no state changes after LOST

assertTrue(client.getZookeeperClient().isConnected());
 

Когда это выполняется, я получаю следующий вывод:

 [Thread-0] INFO org.apache.curator.test.TestingZooKeeperMain - Starting server
[Thread-0] WARN org.apache.zookeeper.server.ServerCnxnFactory - maxCnxns is not configured, using default value 0.
[main] INFO org.apache.curator.framework.imps.CuratorFrameworkImpl - Starting
[main] INFO org.apache.curator.framework.imps.CuratorFrameworkImpl - Default schema
[main] WARN demo.CuratorRecoveryTest - CONNECTION-STATE-CHANGE: null --> CONNECTED
[main] DEBUG demo.CuratorRecoveryTest - Taking a 3s nap for initial settling...
[main] DEBUG demo.CuratorRecoveryTest - Done napping for initial settling...
[Curator-ConnectionStateManager-0] WARN demo.CuratorRecoveryTest - CONNECTION-STATE-CHANGE: CONNECTED --> SUSPENDED
[main] INFO demo.CuratorRecoveryTest - >>>>>>>>>> STOPPED ZK SERVER
[main] DEBUG demo.CuratorRecoveryTest - Taking a 3s nap for letting things bake...
[main] DEBUG demo.CuratorRecoveryTest - Done napping for letting things bake...
[main] DEBUG demo.CuratorRecoveryTest - Taking a 3s nap for disconnecting...
[main] DEBUG demo.CuratorRecoveryTest - Done napping for disconnecting...
[main] INFO demo.CuratorRecoveryTest - >>>>>>>>>> STARTED ZK SERVER
[Curator-ConnectionStateManager-0] WARN org.apache.curator.framework.state.ConnectionStateManager - Session timeout has elapsed while SUSPENDED. Injecting a session expiration. Elapsed ms: 20009. Adjusted session timeout ms: 20000
[main-EventThread] WARN org.apache.curator.ConnectionState - Session expired event received
[Curator-ConnectionStateManager-0] WARN demo.CuratorRecoveryTest - CONNECTION-STATE-CHANGE: SUSPENDED --> LOST
 

что затем завершается неудачей, когда условие ожидания никогда не выполняется.

ПРИМЕЧАНИЕ: Это происходит и в более старой версии комбинации Куратора и Смотрителя зоопарка, так что это не проблема «кровоточащего края».

Чего мне не хватает?

Комментарии:

1. Спасибо, я могу воспроизвести ваш код на github. Но если я удалю server.close и заменю ваш start на a restart , это приведет к успешному повторному подключению клиента. Вероятно server.close , это означает, что тестовый сервер не может быть запущен снова. Возможно, добавьте свою оригинальную дорожку в производство к вашему вопросу? Я испытываю вашу проблему и подозреваю, что это связано с ошибкой в использовании куратором ZooKeeper.updateServerList метода.

2. Этот тест предназначен для имитации отключения сервера zookeeper (возможно, на несколько минут), а затем повторного запуска — не уверен, что предлагаемые вами изменения все еще отражают это. На самом деле нет трассировки стека как таковой, а просто поток сообщений «не удается подключиться» в журнале. Я могу воспроизвести его с помощью реального сервера, но это трудно представить в тесте. Мне придется изучить тот метод, о котором вы упомянули.

3. ОБНОВЛЕНИЕ: Похоже, что это немного отвлекающий маневр. TestingServer Он ведет себя не так, как настоящий сервер, когда он убит и запущен снова. Когда я пытаюсь выполнить тот же сценарий со стандартным сервером ZK, он нормально подключается после потери. Моя проблема, должно быть, исходит откуда-то из моего служебного кода. Я, вероятно, закрою этот вопрос.

4. ОБНОВЛЕНИЕ-2: У меня просто возникла мысль, что причина, по которой сервер тестирования не работает для меня, заключается в том, что он, вероятно, запускается во второй раз на другом порту, чем в первый — мне придется изучить это и опубликовать обновление или закрыть вопрос.

Ответ №1:

У меня была аналогичная проблема, и я пришел к выводу, что куратор, похоже, повторно использует устаревшие IP-адреса при перезапуске сервера zookeeper.

Подход, изложенный в этом билете, сработал для меня. В частности, это фиксация, которая добавляет пользователь ZookeeperFactory , который не использует предыдущий устаревший IP-адрес, а вместо этого использует исходное неразрешенное имя хоста.

Короче говоря, при создании куратора назначьте пользовательский ZookeeperFactory

 CuratorFramework zkClient = CuratorFrameworkFactory
    .builder()
...
    .zookeeperFactory(new ZKClientFactory())
 

где это ZKClientFactory создает новое Zookeeper из кэшированного connectString .