参考:
官文文档:http://curator.apache.org/curator-recipes/leader-latch.html
http://curator.apache.org/curator-recipes/leader-election.html
技术博客: http://blog.csdn.net/smallnest/article/details/41895431
分布式计算中,选举就是指定一个线程用来选择多个节点的领导者。在任务开始时,所有的节点都不知道哪个为领导者。选举算法开始后,领导者选出来后,每个节点通过网络知道领导者的存在。
Curator中选举分为两种: Leader Latch和Leader Election
Leader Latch
很简单的选举算法。随机从候选者中选择一台作为leader, 选中后除非leader自己 调用close()释放leadership,否则其他的候选者不能成为leader
相关的类
- LeaderLatch
使用方法
初始化LeaderLatch
public LeaderLatch(CuratorFramework client,
String latchPath)
Parameters:
client - the client
latchPath - the path for this leadership group
public LeaderLatch(CuratorFramework client,
String latchPath,
String id)
Parameters:
client - the client
latchPath - the path for this leadership group
id - participant ID
启动
通过start()方法启动
leaderLatch.start();
一旦启动,LeaderLatch会与其他有相同latchpath的候选者协商,从中随机选中一个作为leader. 可以调用实例的hasLeadership()判断该实例是否为leader。
public boolean hasLeadership()
Return true if leadership is currently held by this instance
尝试获取leadership
LeaderLatch与JDK的CountDownLatch类似,调用await()方法会使线程一直阻塞到获得leadership为止。
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
Causes the current thread to wait until this instance acquires leadership unless
the thread is interrupted, the specified waiting time elapses or the instance is closed.
Parameters:
timeout - the maximum time to wait
unit - the time unit of the timeout argument
Returns:
true if the count reached zero and false if the waiting time elapsed before the count
reached zero or the instances was closed
释放leadership
只能通过close()释放leadership, 只有leader将leadership释放时,其他的候选者才有机会被选为leader
leaderLatch.close();
错误处理
LeaderLatch通过增加了一个ConnectionStateListener监听连接问题。如果出现SUSPENDED或者LOST,leader会报告自己不再是leader(直到重新建立连接,否则不会有leader)。如果LOST的连接被重新建立即RECONNECTED,leaderLatch会删除先前的zNode并重新建立zNode。
强烈建议LeaderLatch使用时注册一个ConnectionStateListener。
实例
添加依赖
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
<version>2.6.0</version>
</dependency>
代码如下:
package curator;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.test.TestingServer;
import org.apache.curator.utils.CloseableUtils;
import com.google.common.collect.Lists;
public class LeaderLatchExample {
private static final int CLIENT_QTY=10;
private static final String PATH="/examples/leader";
public static void main(String[] args){
List<CuratorFramework> clients=Lists.newArrayList();
List<LeaderLatch> examples=Lists.newArrayList();
TestingServer server = null;
try {
server=new TestingServer();
//创建了10个LeaderLatch,启动后它们中的一个会被选举为leader
for(int i=0;i<CLIENT_QTY;i++){
CuratorFramework client=CuratorFrameworkFactory.newClient(server.getConnectString(),new ExponentialBackoffRetry(1000,3));
clients.add(client);
LeaderLatch example=new LeaderLatch(client,PATH,"client #"+i);
examples.add(example);
client.start();
example.start();
}
// 因为选举会花费一些时间,start后并不能马上就得到leader。
Thread.sleep(20000);
LeaderLatch currentLeader=null;
for(int i=0;i<CLIENT_QTY;i++){
LeaderLatch example=examples.get(i);
//通过hasLeadership查看自己是否是leader
if(example.hasLeadership()){
currentLeader=example;
break;
}
}
System.out.println("current leader is "+currentLeader.getId());
System.out.println("release the leader "+currentLeader.getId());
//只能通过close释放当前的领导权。
currentLeader.close();
//await是一个阻塞方法, 尝试获取leader地位,但是未必能上位。
examples.get(0).await(2,TimeUnit.SECONDS);
System.out.println("client #0 maybe is elected as the leader or not although it want to be");
//可以通过.getLeader().getId()可以得到当前的leader的ID
System.out.println("the new leader is "+examples.get(0).getLeader().getId());
System.out.println("Press enter/return to quit \n");
new BufferedReader(new InputStreamReader(System.in)).readLine();
} catch (Exception e) {
e.printStackTrace();
}finally{
System.out.println("Shutting down....");
for(LeaderLatch exampleClient: examples){
CloseableUtils.closeQuietly(exampleClient);
}
for(CuratorFramework client:clients){
CloseableUtils.closeQuietly(client);
}
CloseableUtils.closeQuietly(server);
}
}
}
首先创建了10个LeaderLatch,启动后它们中的一个会被选举为leader。 因为选举会花费一些时间,start后并不能马上就得到leader。
通过hasLeadership查看自己是否是leader, 如果是的话返回true。
可以通过.getLeader().getId()可以得到当前的leader的ID。
只能通过close释放当前的领导权。
await是一个阻塞方法, 尝试获取leader地位,但是未必能上位。
参考: http://blog.csdn.net/smallnest/article/details/41895431
Leader Election
通过LeaderSelectorListener可以对领导权进行控制, 在适当的时候释放领导权,这样每个节点都有可能获得领导权。 而LeaderLatch一根筋到死, 除非调用close方法,否则它不会释放领导权。
相关的类
- LeaderSelector
- LeaderSelectorListener
- LeaderSelectorListenerAdapter
- CancelLeadershipException
使用方法
创建 LeaderSelector
public LeaderSelector(CuratorFramework client,
String mutexPath,
LeaderSelectorListener listener)
Parameters:
client - the client
mutexPath - the path for this leadership group
listener - listener
public LeaderSelector(CuratorFramework client,
String mutexPath,
ThreadFactory threadFactory,
Executor executor,
LeaderSelectorListener listener)
Parameters:
client - the client
mutexPath - the path for this leadership group
threadFactory - factory to use for making internal threads
executor - the executor to run in
listener - listener
启动
leaderSelector.start();
一旦启动,如果获取了leadership的话,takeLeadership()会被调用,只有当leader释放了leadership的时候,takeLeadership()才会返回。
释放
调用close()释放 leadership
leaderSelector.close();
错误处理
LeaderSelectorListener类继承了ConnectionStateListener。一旦LeaderSelector启动,它会向curator客户端添加监听器。 使用LeaderSelector必须时刻注意连接的变化。一旦出现连接问题如SUSPENDED,curator实例必须确保它可能不再是leader,直至它重新收到RECONNECTED。如果LOST出现,curator实例不再是leader并且其takeLeadership()应该直接退出。
建议重要:推荐的做法是,如果发生SUSPENDED或者LOST连接问题,最好直接抛CancelLeadershipException,此时,leaderSelector实例会尝试中断并且取消正在执行takeLeadership()方法的线程。 建议扩展LeaderSelectorListenerAdapter, LeaderSelectorListenerAdapter中已经提供了推荐的处理方式 。
实例
来自于官方
/**
* 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 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;
/**
* An example leader selector client. Note that {@link LeaderSelectorListenerAdapter} which
* has the recommended handling for connection state issues
*/
public class ExampleClient extends LeaderSelectorListenerAdapter implements Closeable
{
private final String name;
private final LeaderSelector leaderSelector;
private final AtomicInteger leaderCount = new AtomicInteger();
public ExampleClient(CuratorFramework client, String path, String name)
{
this.name = name;
// create a leader selector using the given path for management
// all participants in a given leader selection must use the same path
// ExampleClient here is also a LeaderSelectorListener but this isn't required
leaderSelector = new LeaderSelector(client, path, this);
// for most cases you will want your instance to requeue when it relinquishes leadership
//保证此实例在释放领导权后还可能获得领导权
leaderSelector.autoRequeue();
}
public void start() throws IOException
{
// the selection for this instance doesn't start until the leader selector is started
// leader selection is done in the background so this call to leaderSelector.start() returns immediately
leaderSelector.start();
}
@Override
public void close() throws IOException
{
leaderSelector.close();
}
@Override
public void takeLeadership(CuratorFramework client) throws Exception
{
// we are now the leader. This method should not return until we want to relinquish leadership
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");
}
}
}
其中: leaderSelector.autoRequeue()能保证该实例在释放leadership后还可能重新获得leadership。
成为leader时,会调用 takeLeadership()方法,可以在这里面进行任务分配,并且不要返回,如果想要实例一直是leader可以加一个死循环。
测试代码:来自于官方
/**
* 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 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("Create " + CLIENT_QTY + " clients, have each negotiate for leadership and then wait a random number of seconds before letting another leader election occur.");
System.out.println("Notice that leader election is fair: all clients will become leader and will do so the same number of times.");
List<CuratorFramework> clients = Lists.newArrayList();
List<ExampleClient> 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);
ExampleClient example = new ExampleClient(client, PATH, "Client #" + i);
examples.add(example);
client.start();
example.start();
}
System.out.println("Press enter/return to quit\n");
new BufferedReader(new InputStreamReader(System.in)).readLine();
}
finally
{
System.out.println("Shutting down...");
for ( ExampleClient exampleClient : examples )
{
CloseableUtils.closeQuietly(exampleClient);
}
for ( CuratorFramework client : clients )
{
CloseableUtils.closeQuietly(client);
}
CloseableUtils.closeQuietly(server);
}
}
}
创建了10个客户端,通过公平选举成为leader。