基于curator实现leader选举 博客分类: zookeeper zookeepercuratorleader选举
程序员文章站
2024-03-22 22:43:10
...
1.前言
curator由Netflix的工程师开发,主要目的为了基于zookeeper的应用变得简单可靠,在2013年成为apache的*项目。curator基于zookeeper,但提供了更高级别的
API抽象以及工具集,并对zookeeper提供的常用功能进行了封装和扩充,例如leader选举、分布式锁、服务发现、缓存等功能,从而使开发者在实现这些功能时不用在实现
哪些无聊的程式化代码段,也减少出错的可能性。
2.本文主要讲解如何利用curator实现leader选举,并对curator提供的两种实现形式进行对比
2.1 通过LeaderSelector进行leader选举
建议通过LeaderSelectorListenerAdapter实现,当某个实例成为leader后,会调用对应实例的takeLeadership方法,此方法执行期间,此实例一直占着leader权,
当takeLeadership方法执行结束,实例自动释放leader权,所有实例重新进行leader选举
执行类
执行结果
2.2 通过LeaderLatch实现leader选举,具体实现LeaderLatchListener来实现,
当实例成为leader后,会调用isLeader()方法,之后除非此实例连接不到zookeeper,否侧将一直占着leader权,当失去leader权后会调用notLeader()方法,为了模拟
选举过程,我们追加了一个ScheduledExecutorService来周期性的自己释放leader权
启动类
执行结果
curator由Netflix的工程师开发,主要目的为了基于zookeeper的应用变得简单可靠,在2013年成为apache的*项目。curator基于zookeeper,但提供了更高级别的
API抽象以及工具集,并对zookeeper提供的常用功能进行了封装和扩充,例如leader选举、分布式锁、服务发现、缓存等功能,从而使开发者在实现这些功能时不用在实现
哪些无聊的程式化代码段,也减少出错的可能性。
2.本文主要讲解如何利用curator实现leader选举,并对curator提供的两种实现形式进行对比
2.1 通过LeaderSelector进行leader选举
建议通过LeaderSelectorListenerAdapter实现,当某个实例成为leader后,会调用对应实例的takeLeadership方法,此方法执行期间,此实例一直占着leader权,
当takeLeadership方法执行结束,实例自动释放leader权,所有实例重新进行leader选举
package chengf.falcon.curator.leader; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter; import org.apache.curator.framework.recipes.leader.LeaderSelector; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * @author: 作者: chengaofeng * @date: 创建时间:2018-06-07 15:26:54 * @Description: TODO * @version V1.0 */ public class SelectorClient extends LeaderSelectorListenerAdapter implements Closeable { private final String name; private final LeaderSelector leaderSelector; private final AtomicInteger leaderCount = new AtomicInteger(); public SelectorClient(CuratorFramework client, String path, String name) { this.name = name; // 利用一个给定的路径创建一个leader selector // 执行leader选举的所有参与者对应的路径必须一样 // 本例中SelectorClient也是一个LeaderSelectorListener,但这不是必须的。 leaderSelector = new LeaderSelector(client, path, this); // 在大多数情况下,我们会希望一个selector放弃leader后还要重新参与leader选举 leaderSelector.autoRequeue(); } /** * 启动当前实例参与leader选举 * * @throws IOException */ public void start() throws IOException { // leader选举是在后台处理的,所以这个方法会立即返回 leaderSelector.start(); } @Override public void close() throws IOException { leaderSelector.close(); } /** * 当前实例成为leader时,会执行下面的方法,这个方法执行结束后,当前实例就自动释放leader了,所以在想放弃leader前此方法不能结束 */ @Override public void takeLeadership(CuratorFramework client) throws Exception { final int waitSeconds = (int) (5 * Math.random()) + 1; System.out.println(name + " 现在是leader了,持续成为leader " + waitSeconds + " 秒."); System.out.println(name + " 之前已经成为了 " + leaderCount.getAndIncrement() + " 次leader."); try { Thread.sleep(TimeUnit.SECONDS.toMillis(waitSeconds)); } catch (InterruptedException e) { System.err.println(name + " was interrupted."); Thread.currentThread().interrupt(); } finally { System.out.println(name + " 释放leader权.\n"); } } }
执行类
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package chengf.falcon.curator.leader; import com.google.common.collect.Lists; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingServer; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.List; public class LeaderSelectorExample { private static final int CLIENT_QTY = 10; private static final String PATH = "/examples/leader"; public static void main(String[] args) throws Exception { // all of the useful sample code is in ExampleClient.java System.out.println("创建 " + CLIENT_QTY + " 个客户端, 公平的参与leader选举,成为leader后,会等待一个随机的时间(几秒中),之后释放leader权,所有实例重新进行leader选举。"); List<CuratorFramework> clients = Lists.newArrayList(); List<SelectorClient> examples = Lists.newArrayList(); TestingServer server = new TestingServer(); try { for (int i = 0; i < CLIENT_QTY; ++i) { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000, 3)); clients.add(client); SelectorClient example = new SelectorClient(client, PATH, "Client #" + i); examples.add(example); client.start(); example.start(); } System.out.println("按 enter/return 来退出\n"); new BufferedReader(new InputStreamReader(System.in)).readLine(); } finally { System.out.println("关闭程序..."); for (SelectorClient exampleClient : examples) { CloseableUtils.closeQuietly(exampleClient); } for (CuratorFramework client : clients) { CloseableUtils.closeQuietly(client); } CloseableUtils.closeQuietly(server); } } }
执行结果
创建 10 个客户端, 公平的参与leader选举,成为leader后,会等待一个随机的时间(几秒中),之后释放leader权,所有实例重新进行leader选举。 SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. SLF4J: Failed to load class "org.slf4j.impl.StaticMDCBinder". SLF4J: Defaulting to no-operation MDCAdapter implementation. SLF4J: See http://www.slf4j.org/codes.html#no_static_mdc_binder for further details. 按 enter/return 来退出 Client #2 现在是leader了,持续成为leader 3 秒. Client #2 之前已经成为了 0 次leader. Client #2 释放leader权. Client #4 现在是leader了,持续成为leader 3 秒. Client #4 之前已经成为了 0 次leader. Client #4 释放leader权. Client #5 现在是leader了,持续成为leader 5 秒. Client #5 之前已经成为了 0 次leader. Client #5 释放leader权. Client #9 现在是leader了,持续成为leader 3 秒. Client #9 之前已经成为了 0 次leader. Client #9 释放leader权. Client #6 现在是leader了,持续成为leader 3 秒. Client #6 之前已经成为了 0 次leader. Client #6 释放leader权. Client #1 现在是leader了,持续成为leader 4 秒. Client #1 之前已经成为了 0 次leader. Client #1 释放leader权. Client #8 现在是leader了,持续成为leader 1 秒. Client #8 之前已经成为了 0 次leader. Client #8 释放leader权. Client #7 现在是leader了,持续成为leader 3 秒. Client #7 之前已经成为了 0 次leader. Client #7 释放leader权. Client #0 现在是leader了,持续成为leader 2 秒. Client #0 之前已经成为了 0 次leader. Client #0 释放leader权. Client #3 现在是leader了,持续成为leader 5 秒. Client #3 之前已经成为了 0 次leader. Client #3 释放leader权. Client #2 现在是leader了,持续成为leader 4 秒. Client #2 之前已经成为了 1 次leader. Client #2 释放leader权. Client #4 现在是leader了,持续成为leader 3 秒. Client #4 之前已经成为了 1 次leader. Client #4 释放leader权. Client #5 现在是leader了,持续成为leader 3 秒. Client #5 之前已经成为了 1 次leader. Client #5 释放leader权. Client #9 现在是leader了,持续成为leader 3 秒. Client #9 之前已经成为了 1 次leader. Client #9 释放leader权. Client #6 现在是leader了,持续成为leader 1 秒. Client #6 之前已经成为了 1 次leader. Client #6 释放leader权. Client #1 现在是leader了,持续成为leader 4 秒. Client #1 之前已经成为了 1 次leader. Client #1 释放leader权. Client #8 现在是leader了,持续成为leader 5 秒. Client #8 之前已经成为了 1 次leader. 关闭程序... Client #8 was interrupted. Client #8 释放leader权.
2.2 通过LeaderLatch实现leader选举,具体实现LeaderLatchListener来实现,
当实例成为leader后,会调用isLeader()方法,之后除非此实例连接不到zookeeper,否侧将一直占着leader权,当失去leader权后会调用notLeader()方法,为了模拟
选举过程,我们追加了一个ScheduledExecutorService来周期性的自己释放leader权
/** * */ package chengf.falcon.curator.leader; import java.io.IOException; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.leader.LeaderLatch; import org.apache.curator.framework.recipes.leader.LeaderLatchListener; /** * @author: 作者: chengaofeng * @date: 创建时间:2018-06-07 15:26:54 * @Description: TODO * @version V1.0 */ public class LatchClient implements LeaderLatchListener { private final String name; private LeaderLatch leaderLatch; private CuratorFramework client; public LatchClient(CuratorFramework client, String path, String name) throws Exception { this.name = name; this.client = client; leaderLatch = new LeaderLatch(client, path); leaderLatch.addListener(this); leaderLatch.start(); } /* * (non-Javadoc) * * @see * org.apache.curator.framework.recipes.leader.LeaderLatchListener#isLeader( * ) */ /** * 成为leader后会执行下面的方法,方法执行完后不会释放leader权 */ @Override public void isLeader() { System.out.println(name + " is now the leader. "); } /** * 失去leader后执行下面的方法 */ @Override public void notLeader() { System.out.println(name + " is not the leader. "); } public void stop() { try { leaderLatch.close(); } catch (IOException e) { } } public boolean hasLeadership() { return leaderLatch.hasLeadership(); } public CuratorFramework getClient() { return client; } public String getName() { return name; } }
启动类
/** * */ package chengf.falcon.curator.leader; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingServer; import org.apache.curator.utils.CloseableUtils; import com.google.common.collect.Lists; /** * @author: 作者: chengaofeng * @date: 创建时间:2018-06-07 15:25:47 * @Description: TODO * @version V1.0 */ public class LeaderLatchExample { private static final int CLIENT_QTY = 10; private static final String PATH = "/examples/leader"; public static void main(String[] args) throws Exception { // all of the useful sample code is in ExampleClient.java System.out.println("创建 " + CLIENT_QTY + " 个客户端, 公平的参与leader选举,成为leader后,将一直占用此leader权。直到四秒中后主动放弃leader权"); List<CuratorFramework> clients = Lists.newArrayList(); TestingServer server = new TestingServer(); List<LatchClient> examples = Lists.newArrayList(); int i = 0; try { for (; i < CLIENT_QTY; ++i) { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000, 3)); clients.add(client); LatchClient example = new LatchClient(client, PATH, "Client #" + i); examples.add(example); client.start(); } // 不调用该方法则某实例成为leader后将一直持续占有leader权 ScheduledExecutorService se = giveUpLeader(examples); System.out.println("按 enter/return 来退出\n"); new BufferedReader(new InputStreamReader(System.in)).readLine(); se.shutdown(); } finally { System.out.println("关闭程序..."); for (LatchClient exampleClient : examples) { exampleClient.stop(); } for (CuratorFramework client : clients) { CloseableUtils.closeQuietly(client); } CloseableUtils.closeQuietly(server); } } /** * 主动放弃leader权,并新创建一个实例参与leader选举 * @param examples */ private static ScheduledExecutorService giveUpLeader(List<LatchClient> examples) { ScheduledExecutorService se = Executors.newScheduledThreadPool(1); se.scheduleAtFixedRate(()->{ examples.stream().filter(t-> t.hasLeadership()).findFirst().ifPresent(t-> { examples.remove(t); LatchClient example; try { example = new LatchClient(t.getClient(), PATH, t.getName()); examples.add(example); } catch (Exception e) { } //当前实例主动放弃leader权 System.out.println(t.getName() + " 现在主动放弃leader权"); t.stop();}); }, 4, 4, TimeUnit.SECONDS); return se; } }
执行结果
创建 10 个客户端, 公平的参与leader选举,成为leader后,将一直占用此leader权。直到四秒中后主动放弃leader权 SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. SLF4J: Failed to load class "org.slf4j.impl.StaticMDCBinder". SLF4J: Defaulting to no-operation MDCAdapter implementation. SLF4J: See http://www.slf4j.org/codes.html#no_static_mdc_binder for further details. 按 enter/return 来退出 Client #3 is now the leader. Client #3 现在主动放弃leader权 Client #2 is now the leader. Client #2 现在主动放弃leader权 Client #9 is now the leader. Client #9 现在主动放弃leader权 Client #4 is now the leader. Client #4 现在主动放弃leader权 Client #1 is now the leader. Client #1 现在主动放弃leader权 Client #5 is now the leader. Client #5 现在主动放弃leader权 Client #7 is now the leader. Client #7 现在主动放弃leader权 Client #8 is now the leader. Client #8 现在主动放弃leader权 Client #6 is now the leader. Client #6 现在主动放弃leader权 Client #0 is now the leader. 关闭程序...