美文网首页
Zookeeper客户端Curator使用详解-选举(二)

Zookeeper客户端Curator使用详解-选举(二)

作者: 小陈阿飞 | 来源:发表于2019-01-14 16:51 被阅读40次

    转:http://throwable.coding.me/2018/12/16/zookeeper-curator-usage

    Leader选举

    在分布式计算中, leader elections是很重要的一个功能, 这个选举过程是这样子的: 指派一个进程作为组织者,将任务分发给各节点。 在任务开始前, 哪个节点都不知道谁是leader(领导者)或者coordinator(协调者). 当选举算法开始执行后, 每个节点最终会得到一个唯一的节点作为任务leader. 除此之外, 选举还经常会发生在leader意外宕机的情况下,新的leader要被选举出来。

    在zookeeper集群中,leader负责写操作,然后通过Zab协议实现follower的同步,leader或者follower都可以处理读操作。

    Curator 有两种leader选举的recipe,分别是LeaderSelectorLeaderLatch

    前者是所有存活的客户端不间断的轮流做Leader,大同社会。后者是一旦选举出Leader,除非有客户端挂掉重新触发选举,否则不会交出领导权。某党?

    LeaderLatch

    LeaderLatch有两个构造函数:

    public LeaderLatch(CuratorFramework client, String latchPath)
    public LeaderLatch(CuratorFramework client, String latchPath,  String id)
    
    

    LeaderLatch的启动:

    leaderLatch.start( );

    一旦启动,LeaderLatch会和其它使用相同latch path的其它LeaderLatch交涉,然后其中一个最终会被选举为leader,可以通过hasLeadership方法查看LeaderLatch实例是否leader:

    leaderLatch.hasLeadership( ); //返回true说明当前实例是leader

    类似JDK的CountDownLatch, LeaderLatch在请求成为leadership会block(阻塞),一旦不使用LeaderLatch了,必须调用close方法。 如果它是leader,会释放leadership, 其它的参与者将会选举一个leader。

    public void await() throws InterruptedException,EOFException
    /*Causes the current thread to wait until this instance acquires leadership
    unless the thread is interrupted or closed.*/
    public boolean await(long timeout,TimeUnit unit)throws InterruptedException
    

    异常处理: LeaderLatch实例可以增加ConnectionStateListener来监听网络连接问题。 当 SUSPENDED 或 LOST 时, leader不再认为自己还是leader。当LOST后连接重连后RECONNECTED,LeaderLatch会删除先前的ZNode然后重新创建一个。LeaderLatch用户必须考虑导致leadership丢失的连接问题。 强烈推荐你使用ConnectionStateListener。

    一个LeaderLatch的使用例子:

    public class LeaderLatchDemo extends BaseConnectionInfo {
        protected static String PATH = "/francis/leader";
        private static final int CLIENT_QTY = 10;
    
    
        public static void main(String[] args) throws Exception {
            List<CuratorFramework> clients = Lists.newArrayList();
            List<LeaderLatch> examples = Lists.newArrayList();
            TestingServer server=new TestingServer();
            try {
                for (int i = 0; i < CLIENT_QTY; i++) {
                    CuratorFramework client
                            = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(20000, 3));
                    clients.add(client);
                    LeaderLatch latch = new LeaderLatch(client, PATH, "Client #" + i);
                    latch.addListener(new LeaderLatchListener() {
    
                        @Override
                        public void isLeader() {
                            // TODO Auto-generated method stub
                            System.out.println("I am Leader");
                        }
    
                        @Override
                        public void notLeader() {
                            // TODO Auto-generated method stub
                            System.out.println("I am not Leader");
                        }
                    });
                    examples.add(latch);
                    client.start();
                    latch.start();
                }
                Thread.sleep(10000);
                LeaderLatch currentLeader = null;
                for (LeaderLatch latch : examples) {
                    if (latch.hasLeadership()) {
                        currentLeader = latch;
                    }
                }
                System.out.println("current leader is " + currentLeader.getId());
                System.out.println("release the leader " + currentLeader.getId());
                currentLeader.close();
    
                Thread.sleep(5000);
    
                for (LeaderLatch latch : examples) {
                    if (latch.hasLeadership()) {
                        currentLeader = latch;
                    }
                }
                System.out.println("current leader is " + currentLeader.getId());
                System.out.println("release the leader " + currentLeader.getId());
            } finally {
                for (LeaderLatch latch : examples) {
                    if (null != latch.getState())
                    CloseableUtils.closeQuietly(latch);
                }
                for (CuratorFramework client : clients) {
                    CloseableUtils.closeQuietly(client);
                }
            }
        }
    }
    

    可以添加test module的依赖方便进行测试,不需要启动真实的zookeeper服务端:

    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-test</artifactId>
        <version>2.12.0</version>
    </dependency>
    

    首先我们创建了10个LeaderLatch,启动后它们中的一个会被选举为leader。 因为选举会花费一些时间,start后并不能马上就得到leader。
    通过hasLeadership查看自己是否是leader, 如果是的话返回true。
    可以通过.getLeader().getId()可以得到当前的leader的ID。
    只能通过close释放当前的领导权。
    await是一个阻塞方法, 尝试获取leader地位,但是未必能上位。

    LeaderSelector

    LeaderSelector使用的时候主要涉及下面几个类:

    • LeaderSelector
    • LeaderSelectorListener
    • LeaderSelectorListenerAdapter
    • CancelLeadershipException

    核心类是LeaderSelector,它的构造函数如下:

    public LeaderSelector(CuratorFramework client, String mutexPath,LeaderSelectorListener listener)
    public LeaderSelector(CuratorFramework client, String mutexPath, ThreadFactory threadFactory, Executor executor, LeaderSelectorListener listener)
    
    

    类似LeaderLatch,LeaderSelector必须start: leaderSelector.start(); 一旦启动,当实例取得领导权时你的listener的takeLeadership()方法被调用。而takeLeadership()方法只有领导权被释放时才返回。 当你不再使用LeaderSelector实例时,应该调用它的close方法。

    异常处理 LeaderSelectorListener类继承ConnectionStateListener。LeaderSelector必须小心连接状态的改变。如果实例成为leader, 它应该响应SUSPENDED 或 LOST。 当 SUSPENDED 状态出现时, 实例必须假定在重新连接成功之前它可能不再是leader了。 如果LOST状态出现, 实例不再是leader, takeLeadership方法返回。

    重要: 推荐处理方式是当收到SUSPENDED 或 LOST时抛出CancelLeadershipException异常.。这会导致LeaderSelector实例中断并取消执行takeLeadership方法的异常.。这非常重要, 你必须考虑扩展LeaderSelectorListenerAdapter. LeaderSelectorListenerAdapter提供了推荐的处理逻辑。

    下面的一个例子摘抄自官方:

    public class LeaderSelectorAdapter extends LeaderSelectorListenerAdapter implements Closeable {
        private final String name;
        private final LeaderSelector leaderSelector;
        private final AtomicInteger leaderCount = new AtomicInteger();
    
        public LeaderSelectorAdapter(CuratorFramework client, String path, String name) {
            this.name = name;
            leaderSelector = new LeaderSelector(client, path, this);
            leaderSelector.autoRequeue();
        }
    
        public void start() throws IOException {
            leaderSelector.start();
        }
    
        @Override
        public void close() throws IOException {
            leaderSelector.close();
        }
    
        @Override
        public void takeLeadership(CuratorFramework client) throws Exception {
            final int waitSeconds = (int) (5 * Math.random()) + 1;
            System.out.println(name + " is now the leader. Waiting " + waitSeconds + " seconds...");
            System.out.println(name + " has been leader " + leaderCount.getAndIncrement() + " time(s) before.");
            try {
                Thread.sleep(TimeUnit.SECONDS.toMillis(waitSeconds));
            } catch (InterruptedException e) {
                System.err.println(name + " was interrupted.");
                Thread.currentThread().interrupt();
            } finally {
                System.out.println(name + " relinquishing leadership.\n");
            }
        }
    }
    

    你可以在takeLeadership进行任务的分配等等,并且不要返回,如果你想要要此实例一直是leader的话可以加一个死循环。调用 leaderSelector.autoRequeue();保证在此实例释放领导权之后还可能获得领导权。 在这里我们使用AtomicInteger来记录此client获得领导权的次数, 它是”fair”, 每个client有平等的机会获得领导权。

    public class LeaderSelectorDemo {
    
        protected static String PATH = "/francis/leader";
        private static final int CLIENT_QTY = 10;
    
    
        public static void main(String[] args) throws Exception {
            List<CuratorFramework> clients = Lists.newArrayList();
            List<LeaderSelectorAdapter> examples = Lists.newArrayList();
            TestingServer server = new TestingServer();
            try {
                for (int i = 0; i < CLIENT_QTY; i++) {
                    CuratorFramework client
                            = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(20000, 3));
                    clients.add(client);
                    LeaderSelectorAdapter selectorAdapter = new LeaderSelectorAdapter(client, PATH, "Client #" + i);
                    examples.add(selectorAdapter);
                    client.start();
                    selectorAdapter.start();
                }
                System.out.println("Press enter/return to quit\n");
                new BufferedReader(new InputStreamReader(System.in)).readLine();
            } finally {
                System.out.println("Shutting down...");
                for (LeaderSelectorAdapter exampleClient : examples) {
                    CloseableUtils.closeQuietly(exampleClient);
                }
                for (CuratorFramework client : clients) {
                    CloseableUtils.closeQuietly(client);
                }
                CloseableUtils.closeQuietly(server);
            }
        }
    }
    

    对比可知,LeaderLatch必须调用close()方法才会释放领导权,而对于LeaderSelector,通过LeaderSelectorListener可以对领导权进行控制, 在适当的时候释放领导权,这样每个节点都有可能获得领导权。从而,LeaderSelector具有更好的灵活性和可控性,建议有LeaderElection应用场景下优先使用LeaderSelector。

    相关文章

      网友评论

          本文标题:Zookeeper客户端Curator使用详解-选举(二)

          本文链接:https://www.haomeiwen.com/subject/rbrjdqtx.html