【问题标题】:Application Leader Election with java consul-client使用 java consul-client 进行应用程序领导者选举
【发布时间】:2017-11-15 15:44:24
【问题描述】:

我正在努力了解如何使用 Consul 进行应用程序领导者选举。我正在使用来自 java consul-client 的 LeaderElectionUtil。

我可以选举一个领导者,并且所有节点都同意领导者,但如果领导者应用程序死亡,其他节点似乎不知道并在调用 getLeaderInfoForService 时继续获取死去的领导者 - 即不会发生新的领导者选举。

Leader Electrion Guide (https://www.consul.io/docs/guides/leader-election.html) 提到:

"请注意,默认情况下会话仅使用 gossip 故障检测器。也就是说,只要默认的 Serf 健康检查没有声明节点不健康,就认为会话被节点持有。可以指定其他检查如果需要。”

因此我假设我可能需要向会话添加应用程序级别的健康检查(TTL 等),以便在应用程序失败时会话将失效?这是正确的想法吗?如果是这样,有什么方法可以通过 java 客户端做到这一点?我可以放弃 LeaderElectionUtil 并编写我的代码来选举领导者,但似乎即使在 SessionClient 中也没有办法创建一个带有与之关联的健康检查的会话?

或者也许有更好的方法来实现这一点(领导者重新选举的应用程序级故障检测)?我有点卡住了,所以任何指针都将不胜感激。

【问题讨论】:

    标签: consul


    【解决方案1】:

    所以我解决了它以防其他人遇到这个问题。

    我无法使用 LeaderElectionUtil,但我创建了自己的类来做同样的事情,但在 createSession 方法中我添加了 10 秒的 TTL。

    private String createSession(String serviceName) {
        final Session session = 
    ImmutableSession.builder().name(serviceName).ttl("10s").build();
    
    return client.sessionClient().createSession(session).getId();
    }
    

    为了使其正常工作,您需要有一个后台线程,该线程至少每 10 秒在会话上调用一次 renewSession。

    【讨论】:

    • 那是会话,但根据您上面链接的文档,您还应该尝试获取密钥上的锁。愿意分享相关代码吗?
    【解决方案2】:

    我正在尝试实现相同的要求:我有一个需要选举领导者的 Java 服务,并且我没有在 Consul 中配置服务健康检查。

    使用来自 Consul-client 的 LeaderElectionUtil 是有问题的,因为如果上面提到的所有原因。不幸的是,也无法自定义 LeaderElectionUtil,因为它的所有内部工作都是使用私有方法完成的(例如,它应该使用 protected 并让用户覆盖会话创建)。

    我已尝试按照 consul-client README 中的“基本用法 - 示例 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() 中所做的),consul 的 lock-delay 功能将阻止新领导者被选举约 15 秒(默认值,不幸的是 Consul-client 不提供 API 来修改)。

    为了解决这个问题,除了确保如上所示正确终止服务后自行清理之外,我还确保让服务在所需的最少时间内保持领导位置,并在以下情况下释放领导不再使用它,请致电consul.keyValueClient().releaseLock()。例如,我有一个集群服务,我们选择一个领导者从外部 RDBMS 读取数据更新(然后直接分布在集群中,而不是每个节点重新加载所有数据)。由于这是通过轮询完成的,每个节点都会在轮询之前尝试被选中,如果被选中,它将轮询数据库,传播更新并退出。如果之后崩溃,delay-lock 将不会阻止另一个节点轮询。

    【讨论】:

      【解决方案3】:

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

      • 在 TTL 健康检查中注册
      • 仅将会话绑定到此检查

      相关代码sn-p:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-12
        • 2021-11-08
        • 1970-01-01
        • 2012-11-23
        • 2014-10-04
        • 2018-05-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多