应用程序领导者使用java consul-client进行选举

时间:2016-04-05 05:23:15

标签: consul

我正试图了解如何使用Consul进行应用程序领导者选举。我正在使用java consul-client中的LeaderElectionUtil。

我可以选举领导者,所有节点都同意领导者,但如果领导者申请死亡,其他节点似乎没有意识到并且在调用getLeaderInfoForService时继续让死去的领导者 - 即没有新的领导选举发生。

领袖电子指南(https://www.consul.io/docs/guides/leader-election.html)提到:

"请注意,默认情况下,会话仅使用八卦故障检测器。也就是说,只要默认的Serf运行状况检查未声明节点运行状况不佳,会话就被视为由节点保持。如果需要,可以指定附加检查。"

因此,我假设我可能需要在会话中添加应用程序级别运行状况检查(TTL等),以便在应用程序失败时会话无效?这是正确的想法,如果有的话,有没有办法通过Java客户端这样做?我可以放弃LeaderElectionUtil并编写我的代码来选举一个领导者,但似乎甚至在SessionClient中也没有办法创建一个与之相关的健康检查的会话?

或许有更好的方法来实现这一点(领导者重选的应用程序级别故障检测)?我有点被困,所以任何指针都会受到赞赏。

3 个答案:

答案 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)

如果它仍然相关,我(希望)通过以下方式解决了误报的可能性:

  • 注册TTL健康检查
  • 将会话仅限于此检查

相关代码段:

sessionClient.createSession(
    ImmutableSession.builder()
        .addChecks(checkId) // Ties the session to this check
        .behavior("delete")
        .lockDelay("15s")
        .build()
    )