欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于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选举
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. 

关闭程序...