我正试图了解如何使用Consul进行应用程序领导者选举。我正在使用java consul-client中的LeaderElectionUtil。
我可以选举领导者,所有节点都同意领导者,但如果领导者申请死亡,其他节点似乎没有意识到并且在调用getLeaderInfoForService时继续让死去的领导者 - 即没有新的领导选举发生。
领袖电子指南(https://www.consul.io/docs/guides/leader-election.html)提到:
"请注意,默认情况下,会话仅使用八卦故障检测器。也就是说,只要默认的Serf运行状况检查未声明节点运行状况不佳,会话就被视为由节点保持。如果需要,可以指定附加检查。"
因此,我假设我可能需要在会话中添加应用程序级别运行状况检查(TTL等),以便在应用程序失败时会话无效?这是正确的想法,如果有的话,有没有办法通过Java客户端这样做?我可以放弃LeaderElectionUtil并编写我的代码来选举一个领导者,但似乎甚至在SessionClient中也没有办法创建一个与之相关的健康检查的会话?
或许有更好的方法来实现这一点(领导者重选的应用程序级别故障检测)?我有点被困,所以任何指针都会受到赞赏。
答案 0 :(得分:2)
所以我解决了它,以防其他人遇到这个问题。
我无法使用LeaderElectionUtil但是我创建了自己的类来做同样的事情,但在createSession方法中我添加了一个10s的TTL。
private String createSession(String serviceName) {
final Session session =
ImmutableSession.builder().name(serviceName).ttl("10s").build();
return client.sessionClient().createSession(session).getId();
}
为了使其工作,您需要有一个后台线程,至少每10秒钟在会话上调用一次updateSession。
答案 1 :(得分:1)
我正在尝试实现相同的要求:我有一个需要选举领导者的Java服务,而且我没有在Consul中配置服务运行状况检查。
使用Consul-client的LeaderElectionUtil
是有问题的,因为如果上述所有原因。遗憾的是,也无法自定义LeaderElectionUtil
,因为它的所有内部工作都是使用私有方法完成的(它应该使用protected
并允许用户覆盖会话创建 - 例如)。
我已经尝试实施“服务注册”,如面向客户自述文件中的“基本用法 - 示例1”中所述,但对我来说是calling AgentClient.pass()
always throws an exception。
所以我的解决方案正是你所指定的 - 与TTL进行会话,只要服务还活着就更新它。
这是我的实现,它要求用户同时注册一个回调,用于检查服务是否仍然有效续订,以防万一:
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());
}
}
然后选举领导者或多或少如此简单:
if (consul.keyValueClient().acquireLock(getServiceKey(service), currentNode, sessionHolder.getId()))
return true; // I'm the leader
需要记住的一件事是,如果会话在没有正确清理的情况下终止(我在SessionHolder.close()
中执行的操作),那么领事的lock-delay
功能将阻止选举新的领导者约15秒(默认情况下,不幸的是Consul-client不提供修改的API)。
要解决这个问题,除了确保正确终止服务之外,如上所示,我还要确保服务在所需的最短时间内保持领导地位,并在确定领导时释放领导力。不再使用它,只需拨打consul.keyValueClient().releaseLock()
即可。例如,我有一个集群服务,我们选择一个领导者从外部RDBMS读取数据更新(然后直接在集群中分布,而不是每个节点重新加载所有数据)。由于这是通过轮询完成的,因此每个节点将在轮询之前尝试选举,如果当选,它将轮询数据库,传播更新并辞职。如果在此之后崩溃,delay-lock
将不会阻止其他节点进行轮询。
答案 2 :(得分:0)
如果它仍然相关,我(希望)通过以下方式解决了误报的可能性:
相关代码段:
sessionClient.createSession(
ImmutableSession.builder()
.addChecks(checkId) // Ties the session to this check
.behavior("delete")
.lockDelay("15s")
.build()
)