Как эффективно использовать рецепт LeaderElection с помощью куратора для Zookeeper?
Я использую библиотеку Apache Curator для проведения выборов лидеров в Zookeeper. У меня есть код приложения, развернутый на разных машинах, и мне нужно выполнить мой код с одной машины только потому, что я делаю выборы лидеров в zookeeper, чтобы я мог проверить, являюсь ли я лидером, а затем выполнить этот код.
Ниже мой класс LeaderElectionExecutor
, который гарантирует, что у меня есть один экземпляр куратора для каждого приложения
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.
Ответы
Ответ 1
Как только я решил проблему, очень похожую на вашу. Вот как я это сделал.
Во-первых, у меня были объекты, которыми управлял 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();
}
}
}
}
Поскольку это всего лишь эскиз, я рекомендую вам улучшить этот код, возможно, применить шаблон команды для вызова наблюдателей или даже отправить наблюдателей в пулы потоков, если их работа блокируется или длительный интенсивный процессор задачи.
Ответ 2
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 возвращает фиктивного участника с id "", пока он не выберет лидера.
Ответ 3
Здесь для возрождения старого вопроса...
Это похоже на ответ 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
при указании политики повтора...
Надеюсь, это поможет!
Ответ 4
Я раньше не работал с зоопарком или куратором, поэтому отвечай мой ответ с солью.
Установите флаг.
Boolean isLeaderSelected = false;
В начале защелки установите флаг в значение false.
Когда лидер выбран, установите флаг в true.
В функции isLeader():
isLeader(){
while(!isLeaderSelected){} //waits until leader is selected
//do the rest of the function
}
Это также довольно хакерское решение, но оно должно позволить методу isLeader выполнять, как только это возможно. В случае, если они находятся в разных классах, геттер должен иметь возможность предоставлять isLeaderSelected.