最近使用当当Elastic-job分布式任务调度,了解当当任务具体如何调度执行的,其中涉及到apche Curator客户端选举;对其补充一下这方面知识,curator主要提供两种客户端选举方式LeaderLatch和LeaderSelector
具体使用参考官方http://curator.apache.org/getting-started.html
-
LeaderLatch
通过hasLeadership()查看自己是否是leader, 如果是的话返回true
可以通过.getLeader().getId()可以得到当前的leader的传入自定义的ID
只能通过close释放当前的领导权
await是一个阻塞方法, 尝试获取leader地位,但是未必能上位,最终获取到leader返回
start启动LeaderLatch
LeaderLatch提供两个构造函数://不带ID构造 public LeaderLatch(CuratorFramework client, String latchPath) { this(client, latchPath, "", LeaderLatch.CloseMode.SILENT); } //传入自定义ID的构造 public LeaderLatch(CuratorFramework client, String latchPath, String id) { this(client, latchPath, id, LeaderLatch.CloseMode.SILENT); }
判断是否选举后的leader:public boolean hasLeadership() { return this.state.get() == LeaderLatch.State.STARTED && this.hasLeadership.get(); }
LeaderLatch添加删除监听://添加监听 public void addListener(LeaderLatchListener listener) { this.listeners.addListener(listener); } //带执行器监听 public void addListener(LeaderLatchListener listener, Executor executor) { this.listeners.addListener(listener, executor); } //删除监听 public void removeListener(LeaderLatchListener listener) { this.listeners.removeListener(listener); }
LeaderLatch简单使用: -
public static void main(String[] args) { List<LeaderLatch> leaders = Lists.newArrayList(); List<CuratorFramework> clients = Lists.newArrayList(); try { for (int i = 0; i < 10; i++) { CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.20.34:2181,192.168.20.33:2181", new ExponentialBackoffRetry(1000, 3)); clients.add(client); final LeaderLatch leaderLatch = new LeaderLatch(client, "/motan", "client-" + i); leaderLatch.addListener(new LeaderLatchListener() { @Override public void isLeader() { System.out.println("I am Leader:" + leaderLatch.getId()); } @Override public void notLeader() { System.out.println("I am not Leader"); } }); leaders.add(leaderLatch); client.start(); leaderLatch.start(); } //等待选举结果 Thread.sleep(10000); for (LeaderLatch leaderLatch : leaders) { //判断是否leader if (leaderLatch.hasLeadership()) { System.out.println("===================" + leaderLatch.getId()); } } System.out.println("Press enter/return to quit\n"); new BufferedReader(new InputStreamReader(System.in)).readLine(); } catch (Exception e) { } finally { for (CuratorFramework client : clients) { CloseableUtils.closeQuietly(client); } for (LeaderLatch leaderLatch : leaders) { CloseableUtils.closeQuietly(leaderLatch); } } }
- LeaderSelector
LeaderSelector主要涉及到以下四个类:
LeaderSelector
LeaderSelectorListener
LeaderSelectorListenerAdapter
CancelLeadershipException
start/close和LeaderLatch一样需要启动关闭才行
leaderSelector.autoRequeue保证在此实例释放领导权之后还可能获得领导权
takeLeadership方法如果想要某个被选举后的leader一直保持不变,将takeLeadership方法体内容加入死循环即可
LeaderSelector简单使用:package com.lianpo.rpc.zk.curator; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.leader.LeaderSelector; import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.utils.CloseableUtils; import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import lombok.Getter; /** * Created by liz on 2017/1/13. * * @auther liz */ @Getter public class CuratorFrame { private CuratorFramework client; public void init() { CuratorFrameworkFactory.Builder Builder = CuratorFrameworkFactory.builder() .connectString("192.168.20.34:2181") .retryPolicy(new ExponentialBackoffRetry(1000, 3, 3000)) //.namespace("") .sessionTimeoutMs(3000) .connectionTimeoutMs(3000); client = Builder.build(); /*client.start(); try { if (!client.blockUntilConnected(1000 * 3, TimeUnit.MILLISECONDS)) { client.close(); throw new KeeperException.OperationTimeoutException(); } //CHECKSTYLE:OFF } catch (final Exception ex) { ex.printStackTrace(); }*/ } //1、Recipes模块:Elections(选举),Locks(锁),Barriers(关卡),Atomic(原子量),Caches,Queues等 /** * 本类基于leaderSelector实现,所有存活的client会公平的轮流做leader * 如果不想频繁的变化Leader,需要在takeLeadership方法里阻塞leader的变更! 或者使用 {@link} * LeaderLatchClient */ class LeaderSelectorClient extends LeaderSelectorListenerAdapter implements Closeable { private final String name; private final LeaderSelector leaderSelector; private final String PATH = "/motan"; private final AtomicInteger leaderCount = new AtomicInteger(1); public LeaderSelectorClient(CuratorFramework client, String name) { this.name = name; this.leaderSelector = new LeaderSelector(client, PATH, this); //设置一下随机ID this.leaderSelector.setId(UUID.randomUUID().toString()); //保证此实例在释放领导权后还可能获得领导权 this.leaderSelector.autoRequeue(); } @Override public void close() throws IOException { this.leaderSelector.close(); } public void start() throws IOException { this.leaderSelector.start(); } @Override public void takeLeadership(CuratorFramework client) throws Exception { int waitSeconds = (int) (5 * Math.random() + 1); System.out.println(name + ":是当前客户端leader,选举次数:" + leaderCount.getAndIncrement() + ",选举ID:" + this.leaderSelector.getId()); try { Thread.sleep(TimeUnit.SECONDS.toMillis(waitSeconds)); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } finally { System.out.println(name + ":让出领导权,选举ID:" + this.leaderSelector.getId()); } } } //2、locks public static void main(String[] args) { List<CuratorFramework> clients = new ArrayList<CuratorFramework>(); List<LeaderSelectorClient> selectorClients = new ArrayList<LeaderSelectorClient>(); try { for (int i = 0; i < 10; i++) { CuratorFrame curatorFrame = new CuratorFrame(); curatorFrame.init(); clients.add(curatorFrame.client); CuratorFrame.LeaderSelectorClient leaderSelectorClient = curatorFrame.new LeaderSelectorClient(curatorFrame.client, "client#" + i); selectorClients.add(leaderSelectorClient); curatorFrame.client.start(); leaderSelectorClient.start(); //Stat stat = curatorFrame.client.checkExists().forPath("/motan"); //System.out.println("----------------------------stat--" + stat); } System.out.println("----------先观察一会选举的结果-----------"); Thread.sleep(10000); System.out.println("----------关闭前5个客户端,再观察选举的结果-----------"); for (int i = 0; i < 5; i++) { clients.get(i).close(); } System.out.println("--------------------------------------------------"); // 这里有个小技巧,让main程序一直监听控制台输入,异步的代码就可以一直在执行。不同于while(ture)的是,按回车或esc可退出 new BufferedReader(new InputStreamReader(System.in)).readLine(); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("===================finally======================="); for (LeaderSelectorClient selectorClient : selectorClients) { CloseableUtils.closeQuietly(selectorClient); } for (CuratorFramework client : clients) { CloseableUtils.closeQuietly(client); } } } }
总结:LeaderSelector与LeaderLatch, 通过LeaderSelectorListener
可以对领导权进行控制, 在适当的时候释放领导权,这样每个节点都有可能获得领导权。 而LeaderLatch一根筋到死, 除非调用close方法,否则它不会释放领导权