• 04.Curator Leader选举


        在分布式计算中,leader election是很重要的一个功能,这个选举过程是这样子的:指派一个进程作为组织者,将任务分发给各节点。在任务开始前,哪个节点都不知道谁是leader或者coordinator。当选举算法开始执行后,每个节点最终会得到一个唯一的节点作为任务leader。除此之外,选举还经常会发生在leader意外宕机的情况下,新的leader要被选举出来。
        Curator有两种选举recipe,你可以根据你的需求选择合适的。

    1.Leader Latch

    1.LeaderLatch的简单介绍
        首先我们看一个使用LeaderLatch类来选举的例子。它的常用方法如下:
    1. // 构造方法
    2. public LeaderLatch(CuratorFramework client, String latchPath)
    3. public LeaderLatch(CuratorFramework client, String latchPath, String id)
    4. public LeaderLatch(CuratorFramework client, String latchPath, String id, CloseMode closeMode)
    5. // 查看当前LeaderLatch实例是否是leader
    6. public boolean hasLeadership()
    7. // 尝试让当前LeaderLatch实例称为leader
    8. public void await() throws InterruptedException, EOFException
    9. public boolean await(long timeout, TimeUnit unit) throws InterruptedException
    必须启动LeaderLatch: leaderLatch.start();一旦启动,LeaderLatch会和其它使用相同latch path的其它LeaderLatch交涉,然后随机的选择其中一个作为leader。
    一旦不使用LeaderLatch了,必须调用close方法。如果它是leader,会释放leadership,其它的参与者将会选举一个leader。
    2.异常处理
        LeaderLatch实例可以增加ConnectionStateListener来监听网络连接问题。当 SUSPENDED 或 LOST 时,leader不再认为自己还是leader.当LOST 连接重连后 RECONNECTED,LeaderLatch会删除先前的ZNode然后重新创建一个.
        LeaderLatch用户必须考虑导致leadershi丢失的连接问题。强烈推荐你使用ConnectionStateListener。
    3.示例程序
    1. public class LeaderLatchExample
    2. {
    3. private static final int CLIENT_QTY = 10;
    4. private static final String PATH = "/examples/leader";
    5. public static void main(String[] args) throws Exception
    6. {
    7. List<CuratorFramework> clients = Lists.newArrayList();
    8. List<LeaderLatch> examples = Lists.newArrayList();
    9. try
    10. {
    11. for (int i = 0; i < CLIENT_QTY; ++i)
    12. {
    13. CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
    14. clients.add(client);
    15. client.start();
    16. LeaderLatch example = new LeaderLatch(client, PATH, "Client #" + i);
    17. examples.add(example);
    18. example.start();
    19. }
    20. System.out.println("LeaderLatch初始化完成!");
    21. Thread.sleep(10 * 1000);// 等待Leader选举完成
    22. LeaderLatch currentLeader = null;
    23. for (int i = 0; i < CLIENT_QTY; ++i)
    24. {
    25. LeaderLatch example = examples.get(i);
    26. if (example.hasLeadership())
    27. {
    28. currentLeader = example;
    29. }
    30. }
    31. System.out.println("当前leader:" + currentLeader.getId());
    32. currentLeader.close();
    33. examples.get(0).await(10, TimeUnit.SECONDS);
    34. System.out.println("当前leader:" + examples.get(0).getLeader());
    35. System.out.println("输入回车退出");
    36. new BufferedReader(new InputStreamReader(System.in)).readLine();
    37. }
    38. catch (Exception e)
    39. {
    40. e.printStackTrace();
    41. }
    42. finally
    43. {
    44. for (LeaderLatch exampleClient : examples)
    45. {
    46. System.out.println("当前leader:" + exampleClient.getLeader());
    47. try
    48. {
    49. CloseableUtils.closeQuietly(exampleClient);
    50. }
    51. catch (Exception e)
    52. {
    53. System.out.println(exampleClient.getId() + " -- " + e.getMessage());
    54. }
    55. }
    56. for (CuratorFramework client : clients)
    57. {
    58. CloseableUtils.closeQuietly(client);
    59. }
    60. }
    61. System.out.println("OK!");
    62. }
    63. }
    首先我们创建了10个LeaderLatch,启动后它们中的一个会被选举为leader。因为选举会花费一些时间,start后并不能马上就得到leader。
    通过hasLeadership查看自己是否是leader,如果是的话返回true。
    可以通过.getLeader().getId()可以得到当前的leader的ID。
    只能通过close释放当前的领导权。
    await是一个阻塞方法, 尝试获取leader地位,但是未必能上位。
    注意:LeaderLatch类不能close()多次,LeaderLatch.hasLeadership()LeaderLatch.getLeader()得到的结果不一定一致,需要通过LeaderLatch.getLeader().isLeader()来判断。
    4.运行结果及其分析
        上面测试程序运行结果如下:
    1. LeaderLatch初始化完成!
    2. 当前leaderClient #1
    3. 当前leaderParticipant{id='Client #8', isLeader=true}
    4. 输入回车退出
    5. 当前leaderParticipant{id='Client #8', isLeader=true}
    6. 当前leaderParticipant{id='Client #8', isLeader=true}
    7. Client #1 -- Already closed or has not been started
    8. 当前leaderParticipant{id='Client #8', isLeader=true}
    9. 当前leaderParticipant{id='Client #8', isLeader=true}
    10. 当前leaderParticipant{id='Client #8', isLeader=true}
    11. 当前leaderParticipant{id='Client #8', isLeader=true}
    12. 当前leaderParticipant{id='Client #8', isLeader=true}
    13. 当前leaderParticipant{id='Client #8', isLeader=true}
    14. 当前leaderParticipant{id='Client #8', isLeader=true}
    15. 当前leaderParticipant{id='Client #9', isLeader=true}
    16. OK!
    使用ZooInspector工具查看Zookeeper数据如下图:

    每创建一个LeaderLatch实例并调用其start()方法就会在其Path下创建一个节点,当调用close()方法时就会删除节点。

    2.Leader Election

        Curator还提供了另外一种选举方法。与Leader latch不同的是这种方法可以对领导权进行控制,在适当的时候释放领导权,这样每个节点都有可能获得领导权。主要涉及以下四个类:
    • LeaderSelector - 选举Leader的角色。
    • LeaderSelectorListener - 选举Leader时的事件监听。
    • LeaderSelectorListenerAdapter - 选举Leader时的事件监听,官方提供的适配器,用于用户扩展。
    • CancelLeadershipException - 取消Leader权异常
    1.主要类介绍
        重要的是LeaderSelector类,它的构造函数为:
    1. public LeaderSelector(CuratorFramework client, String leaderPath, LeaderSelectorListener listener)
    2. public LeaderSelector(CuratorFramework client, String leaderPath, ExecutorService executorService, LeaderSelectorListener listener)
    3. public LeaderSelector(CuratorFramework client, String leaderPath, CloseableExecutorService executorService, LeaderSelectorListener listener)
        类似LeaderLatch,必须start: leaderSelector.start();一旦启动,当实例取得领导权时LeaderSelectorListener的takeLeadership()方法被调用。而takeLeadership()方法执行完毕时领导权会自动释放重新选举当你不再使用LeaderSelector实例时,应该调用它的close方法。LeaderSelector类中也有hasLeadership()、getLeader()方法。
    2.异常处理
        LeaderSelectorListener类继承ConnectionStateListener。LeaderSelector必须小心连接状态的改变。如果实例成为leader,它应该相应SUSPENDED 或 LOST。当 SUSPENDED 状态出现时,实例必须假定在重新连接成功之前它可能不再是leader了。如果LOST状态出现,实例不再是leader,takeLeadership方法返回.
        注意: 推荐处理方式是当收到SUSPENDED 或 LOST时抛出CancelLeadershipException异常. 这会导致LeaderSelector实例中断并取消执行takeLeadership方法的异常。这非常重要,你必须考虑扩展LeaderSelectorListenerAdapter。LeaderSelectorListenerAdapter提供了推荐的处理逻辑。
    3.示例程序
        首先创建一个ExampleClient类,它继承LeaderSelectorListenerAdapter,它实现了takeLeadership方法:
    1. public class ExampleClient extends LeaderSelectorListenerAdapter implements Closeable
    2. {
    3. private final String name;
    4. private final LeaderSelector leaderSelector;
    5. private final AtomicInteger leaderCount = new AtomicInteger();
    6. public ExampleClient(CuratorFramework client, String path, String name)
    7. {
    8. this.name = name;
    9. leaderSelector = new LeaderSelector(client, path, this);
    10. leaderSelector.autoRequeue();
    11. }
    12. public void start() throws IOException
    13. {
    14. leaderSelector.start();
    15. }
    16. @Override
    17. public void close() throws IOException
    18. {
    19. leaderSelector.close();
    20. }
    21. @Override
    22. public void takeLeadership(CuratorFramework client) throws Exception
    23. {
    24. final int waitSeconds = 1;
    25. System.out.println(name + " 是当前的leader(" + leaderSelector.hasLeadership() + ") 等待" + waitSeconds + "秒...");
    26. System.out.println(name + " 之前成为leader的次数:" + leaderCount.getAndIncrement() + "次");
    27. try
    28. {
    29. Thread.sleep(TimeUnit.SECONDS.toMillis(waitSeconds));
    30. }
    31. catch (InterruptedException e)
    32. {
    33. System.err.println(name + " 已被中断");
    34. Thread.currentThread().interrupt();
    35. }
    36. finally
    37. {
    38. System.out.println(name + " 放弃leader ");
    39. }
    40. }
    41. }
        你可以在takeLeadership方法中进行任务的分配等业务处理,并且不要返回(一返回就会释放Leader权),如果你想要要此实例一直是leader的话可以加一个死循环。leaderSelector.autoRequeue();保证在此实例释放领导权之后还可能获得领导权。在这里我们使用AtomicInteger来记录此client获得领导权的次数,每个client有平等的机会获得领导权。
        测试代码:
    1. public class LeaderSelectorExample
    2. {
    3. private static final int CLIENT_QTY = 10;
    4. private static final String PATH = "/examples/leader";
    5. public static void main(String[] args) throws Exception
    6. {
    7. List<CuratorFramework> clients = Lists.newArrayList();
    8. List<ExampleClient> examples = Lists.newArrayList();
    9. try
    10. {
    11. for (int i = 0; i < CLIENT_QTY; ++i)
    12. {
    13. CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
    14. clients.add(client);
    15. client.start();
    16. ExampleClient example = new ExampleClient(client, PATH, "Client #" + i);
    17. examples.add(example);
    18. example.start();
    19. }
    20. System.out.println("输入回车退出:");
    21. new BufferedReader(new InputStreamReader(System.in)).readLine();
    22. }
    23. finally
    24. {
    25. for (ExampleClient exampleClient : examples)
    26. {
    27. CloseableUtils.closeQuietly(exampleClient);
    28. }
    29. for (CuratorFramework client : clients)
    30. {
    31. CloseableUtils.closeQuietly(client);
    32. }
    33. }
    34. System.out.println("OK!");
    35. }
    36. }
    4.示例运行结果及分析
        运行结果控制台:
    1. 输入回车退出:
    2. Client #4 是当前的leader(true) 等待1秒...
    3. Client #4 之前成为leader的次数:0
    4. Client #4 放弃leader
    5. Client #5 是当前的leader(true) 等待1秒...
    6. Client #5 之前成为leader的次数:0
    7. Client #5 已被中断
    8. Client #5 放弃leader
    9. OK!
    可以看出:LeaderSelector与LeaderLatch的区别,通过LeaderSelectorListener可以对领导权进行控制,在适当的时候释放领导权,这样每个节点都有可能获得领导权。而LeaderLatch一根筋到死,除非调用close方法,否则它不会释放领导权。
    -------------------------------------------------------------------------------------------------------------------------------



  • 相关阅读:
    异步IO数据库队列缓存
    Python终端如何输出彩色字体
    pycharm导入本地py文件时,模块下方出现红色波浪线时如何解决
    Python中字典的详细用法
    基于pandas数据预处理基础操作
    进程、线程、协程篇
    面向对象编程进阶
    常用sign算法
    Excel随机生成数据2
    Oracle dbms_random随机数包详解
  • 原文地址:https://www.cnblogs.com/LiZhiW/p/4930486.html
Copyright © 2020-2023  润新知