Sto cercando di implementare lo stesso requisito: ho un servizio Java che deve eleggere un leader e non ho i controlli di integrità del servizio configurati in Consul.
L'utilizzo di LeaderElectionUtil
da parte di Consul-client è problematico perché se tutti i motivi indicati sopra. Purtroppo non è nemmeno possibile personalizzare LeaderElectionUtil
perché tutti i suoi meccanismi interni vengono eseguiti utilizzando metodi privati (dovrebbe aver utilizzato protected
e consentire agli utenti di sovrascrivere la creazione della sessione, ad esempio).
Ho provato a implementare "Registrazione servizio" come documentato in "Uso di base - Esempio 1" nel README consul-client, ma per me calling AgentClient.pass()
always throws an exception.
Quindi la mia soluzione è esattamente ciò che hai specificato: avere una sessione con un TTL e rinnovarla finché il servizio è attivo.
Ecco la mia realizzazione, che richiede all'utente di registrare anche un callback che viene utilizzato per controllare se il servizio è ancora valido per il rinnovo, nel caso in cui:
public class SessionHolder implements Runnable {
private static final String TTL_TEMPLATE = "%ss";
private Consul client;
private String id;
private LinkedList<Supplier<Boolean>> liveChecks = new LinkedList<>();
private long ttl;
private boolean shutdown = false;
public SessionHolder(Consul client, String service, long ttl) {
this.client = client;
this.ttl = ttl;
final Session session = ImmutableSession.builder()
.name(service)
.ttl(String.format(TTL_TEMPLATE, ttl))
.build();
id = client.sessionClient().createSession(session).getId();
Thread upkeep = new Thread(this);
upkeep.setDaemon(true);
upkeep.start();
}
public String getId() {
return id;
}
public void registerKeepAlive(Supplier<Boolean> liveCheck) {
liveChecks.add(liveCheck);
}
@Override
public synchronized void run() {
// don't start renewing immediately
try {
wait(ttl/2 * 1000);
} catch (InterruptedException e) {}
while (!isShutdown()) {
if (liveChecks.isEmpty() || liveChecks.stream().allMatch(Supplier::get)) {
client.sessionClient().renewSession(getId());
}
try {
wait(ttl/2 * 1000);
} catch (InterruptedException e) {
// go on, try again
}
}
}
public synchronized boolean isShutdown() {
return shutdown;
}
public synchronized void close() {
shutdown = true;
notify();
client.sessionClient().destroySession(getId());
}
}
Poi eleggere un leader è più o meno semplice come:
if (consul.keyValueClient().acquireLock(getServiceKey(service), currentNode, sessionHolder.getId()))
return true; // I'm the leader
una cosa che deve ricordare, è che se la sessione termina senza ripulire correttamente (quello che faccio sopra SessionHolder.close()
), la funzione lock-delay
della console impedirà un nuovo leader da eleggere f o circa 15 secondi (l'impostazione predefinita, che sfortunatamente Consul-client non offre un'API da modificare).
Per risolvere questo problema, oltre a fare in modo che i servizi di terminazione correttamente si ripuliscano da soli come dimostrato sopra, mi assicuro inoltre che il servizio mantenga la posizione di leader per la quantità minima di tempo necessaria e per rilasciare la leadership quando non lo si utilizza più, chiamando consul.keyValueClient().releaseLock()
. Ad esempio, ho un servizio in cluster in cui eleggiamo un leader per leggere gli aggiornamenti dei dati da un RDBMS esterno (che vengono quindi distribuiti direttamente nel cluster anziché ogni nodo che ricarica tutti i dati). Dato che questo viene fatto tramite il polling, ogni nodo cercherà di essere eletto prima del polling e, se eletto, interrogherà il database, diffonderà aggiornamenti e dimissioni. In caso contrario, delay-lock
non impedirà il polling di un altro nodo.
Questa è la sessione, ma secondo il documento che hai linkato sopra, devi d anche provare ad acquisire un blocco su una chiave. Cura di condividere il codice rilevante per questo? – Guss