Как эффективно использовать рецепт LeaderElection с помощью Curator for Zookeeper?

Я использую библиотеку Apache Curator для проведения выборов руководства в Zookeeper. У меня есть код приложения, развернутый на разных машинах, и мне нужно выполнить мой код только с одной машины, поэтому я провожу выборы лидера в зоопарке, чтобы я мог проверить, являюсь ли я лидером, а затем выполнить этот код.

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

public class LeaderElectionExecutor {

    private ZookeeperClient zookClient;

    private static final String LEADER_NODE = "/testleader";

    private static class Holder {
        static final LeaderElectionExecutor INSTANCE = new LeaderElectionExecutor();
    }

    public static LeaderElectionExecutor getInstance() {
        return Holder.INSTANCE;
    }

    private LeaderElectionExecutor() {
        try {
            String hostname = Utils.getHostName();

            String nodes = "host1:2181,host2:2181;

            zookClient = new ZookeeperClient(nodes, LEADER_NODE, hostname);
            zookClient.start();

            // added sleep specifically for the leader to get selected
            // since I cannot call isLeader method immediately after starting the latch
            TimeUnit.MINUTES.sleep(1);
        } catch (Exception ex) {
            // logging error
            System.exit(1);
        }
    }

    public ZookeeperClient getZookClient() {
        return zookClient;
    }
}

А ниже мой код ZookeeperClient -

// can this class be improved in any ways?
public class ZookeeperClient {

    private CuratorFramework client;
    private String latchPath;
    private String id;
    private LeaderLatch leaderLatch;

    public ZookeeperClient(String connString, String latchPath, String id) {
        client = CuratorFrameworkFactory.newClient(connString, new ExponentialBackoffRetry(1000, Integer.MAX_VALUE));
        this.id = id;
        this.latchPath = latchPath;
    }

    public void start() throws Exception {
        client.start();
        leaderLatch = new LeaderLatch(client, latchPath, id);
        leaderLatch.start();
    }

    public boolean isLeader() {
        return leaderLatch.hasLeadership();
    }

    public Participant currentLeader() throws Exception {
        return leaderLatch.getLeader();
    }

    public void close() throws IOException {
        leaderLatch.close();
        client.close();
    }

    public CuratorFramework getClient() {
        return client;
    }

    public String getLatchPath() {
        return latchPath;
    }

    public String getId() {
        return id;
    }

    public LeaderLatch getLeaderLatch() {
        return leaderLatch;
    }
}

Теперь в моем приложении я использую такой код:

public void method01() {
    ZookeeperClient zookClient = LeaderElectionExecutor.getInstance().getZookClient();
    if (zookClient.isLeader()) {
        // do something
    }
}

public void method02() {
    ZookeeperClient zookClient = LeaderElectionExecutor.getInstance().getZookClient();
    if (zookClient.isLeader()) {
        // do something
    }
}

Постановка проблемы:-

В библиотеке Куратора - Вызов isLeader() сразу после запуска защелки не сработает. Чтобы выбрать лидера, нужно время. И только по этой причине я добавил 1 минуту сна в свой код LeaderElectionExecutor, который отлично работает, но я думаю, что это неправильный способ сделать это.

Есть ли лучший способ сделать это? Имея это в виду, мне нужен способ проверить, являюсь ли я лидером, а затем выполнить этот фрагмент кода. Я не могу сделать все в одном методе, поэтому мне нужно вызвать метод isLeader из разных классов и методов, чтобы проверить, являюсь ли я лидером, а затем выполнить только этот фрагмент кода.

Я использую версию Zookeeper 3.4.5 и Curator 1.7.1.


person john    schedule 18.01.2015    source источник


Ответы (4)


Однажды я решил проблему, очень похожую на вашу. Вот как я это сделал.

Во-первых, мои объекты управлялись Spring. Итак, у меня был LeaderLatch, который можно было вводить через контейнер. Одним из компонентов, использовавших LeaderLatch, был LeadershipWatcher, реализация интерфейса Runnable, которая отправляла событие лидерства другим компонентам. Эти последние компоненты были реализациями интерфейса, который я назвал LeadershipObserver. Реализация LeadershipWatcher была в основном похожа на следующий код:

@Component
public class LeadershipWatcher implements Runnable {
  private final LeaderLatch leaderLatch;
  private final Collection<LeadershipObserver> leadershipObservers;

  /* constructor with @Inject */

  @Override
  public void run() {
    try {
      leaderLatch.await();

      for (LeadershipObserver observer : leadershipObservers) {
        observer.granted();
      }
    } catch (InterruptedException e) {
      for (LeadershipObserver observer : leadershipObservers) {
        observer.interrupted();
      }
    }
  }
}

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

person PEdroArthur    schedule 13.02.2015

Я раньше не работал с смотрителем зоопарка или куратором, так что отнеситесь к моему ответу с долей скептицизма.

Установить флаг.

Логическое значение isLeaderSelected = false;

В начале защелки установите флаг в значение false. Когда лидер выбран, установите флаг в значение true.

В функции isLeader():

isLeader(){
while(!isLeaderSelected){} //waits until leader is selected

//do the rest of the function
}

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

person NetherMole    schedule 08.02.2015

leaderLatch = new LeaderLatch(curatorClient, zkPath, String.valueOf(new Random().nextInt()));
leaderLatch.start();
Participant participant;
while(true) {
  participant = leaderLatch.getLeader();
  // Leader election happens asynchronously after calling start, this is a hack to wait until election happens
  if (!(participant.getId().isEmpty() || participant.getId().equalsIgnoreCase(""))) {
    break;
  }
}
if(leaderLatch.hasLeadership()) {
...
}

Обратите внимание, что getLeader возвращает фиктивного участника с идентификатором "" до тех пор, пока не будет выбран лидер.

person srav    schedule 31.08.2015

Вот, чтобы возродить старый вопрос...

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

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

    RetryPolicy retryPolicy = _client.getZookeeperClient().getRetryPolicy();
    RetrySleeper awaitLeadership = _leaderLatch::await;

    final long start = System.currentTimeMillis();
    int count = 0;

    do {
        try {
            // curator will return a dummy leader in the case when a leader has
            // not yet actually been elected. This dummy leader will have isLeader
            // set to false, so we need to check that we got a true leader
            if (_leaderLatch.getLeader().isLeader()) {
                return;
            }
        } catch (KeeperException.NoNodeException e) {
            // this is the case when the leader node has not yet been created
            // by any client - this is fine because we are still waiting for
            // the algorithm to start up so we ignore the error
        }
    } while (retryPolicy.allowRetry(count++, System.currentTimeMillis() - start, awaitLeadership));

    // we have exhausted the retry policy and still have not elected a leader
    throw new IOException("No leader was elected within the specified retry policy!");

Хотя взглянув на вашу инициализацию CuratorFramework, я бы предостерег от использования Integer.MAX_VALUE при указании политики повтора...

Надеюсь, это поможет!

person Almog    schedule 10.05.2017