模拟leader选举:
1、zookeeper服务器上有一个/leader节点
2、在/leader节点下创建短暂顺序节点/leader/lock-xxxxxxx
3、获取/leader的所有子节点并注册监听
4、拿自己的顺序号跟其他子节点的顺序号比较,如果自己的是最小的则获得leader
5、监听到/leader子节点发生变化则执行步骤3、 4尝试获取leader
 
Client .java 
package leader;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
/**
* 模拟leader选举
*
* 1、zookeeper服务器上有一个/leader节点
* 2、在/leader节点下创建短暂顺序节点/leader/lock-xxxxxxx
* 3、获取/leader的所有子节点并注册监听
* 4、拿自己的顺序号跟其他子节点的顺序号比较,如果自己的是最小的则获得leader
* 5、监听到/leader子节点发生变化则执行步骤3、 4尝试获取leader
*
*/
public class Client { public static final String HOSTS = "hadoop1:2181";
public static final String LEADER_PATH = "/leader"; private String path = null; public void run() throws Exception {
ZooKeeper zk = ZKUtils.open(HOSTS);
path = zk.create(LEADER_PATH + "/lock-", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); getLeader(zk); TimeUnit.DAYS.sleep(1);
}
private void getLeader(final ZooKeeper zk)
throws KeeperException, InterruptedException {
//获取/leader下的所有子节点
//注册/leader子节点观察
List<String> children = zk.getChildren(LEADER_PATH, new Watcher() {
@Override
public void process(WatchedEvent event) {
//如果/leader子节点发生变化,则再进行一次getLeader
if(event.getType().equals(EventType.NodeChildrenChanged)) {
try {
getLeader(zk);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}); //拿自己的顺序号跟/leader所有子节点的顺序号比较,如果是最小的则拿到leader
long seq = getSeq(path);
boolean isMin = true;
for (String child : children) {
long childSeq = getSeq(child);
if(childSeq < seq) {
isMin = false;
break;
}
}
if(isMin) {
System.out.printf("我拿到锁了, path: %s, thread: %s", path, Thread.currentThread().getName() );
}
} public long getSeq(String path) {
return Long.parseLong(path.split("-")[1]);
}
}

  

Test .java

package leader;
public class Test {
//多次执行这个类,模拟多个客户端竞选leader
public static void main(String[] args) throws Exception {
new Client().run();
}
}

  

其实在上面的实现中存在几个问题:

1、羊群效应
    在上面的实现中,监听的是/leader的子节点变化,每当一个子节点添加或删除的时候都会通知所有客户端(客户端监听了子节点变化),客户端开始竞争leader,如果客户端成百上千个,这样形成了瞬间峰值流量,对zookeeper服务器造成压力。
    而其实,并不是所有的客户端都需要对某个子节点变化进行处理,服务器只需要通知被删除节点的下一个节点即可,而添加新节点不需要通知客户端。
 
2、重试问题
    客户端在创建暂时顺序节点时不能处理因连接丢失导致的失败,因为客户端没法知道create操作是成功还是失败,create操作不是幂等操作(多次create会创建多个节点),不能进行重试。
    解决方法是给将要创建的节点一个标识符,以表明是我这个客户端创建的,通常使用sessionid来标识,最终创建的节点名称形如lock-<sessionid>-<sequenceNumber>,重试时,查询/leader是否存在包含<sessionid>的子节点,没有再创建。
 
    由此可见,创建高效可靠的分布式锁是多么的困难,zookeeper的recipes目录下有个WriteLock锁实现,在生产环境下也可使用。
 
 
参考资料:《Hadoop权威指南(第二版)》

最新文章

  1. SQL语句实现Split并合并查询结果
  2. 【adb】adb基本命令总结
  3. ssh authentication魔鬼细节--.ssh文件夹权限
  4. .Net中的Debug模式和Release模式
  5. Codeforces Codeforces Round #316 (Div. 2) C. Replacement set
  6. 打印出1,11,21,31,41。。。。。。的shell脚本
  7. JAVA中日期处理
  8. Wisdombud.CommonTool及其应用
  9. iOS中—触摸事件详解及使用
  10. 从零教你在Linux环境下(ubuntu)如何编译hadoop2.4
  11. Javascript 运动中Offset的bug——逐行分析代码,让你轻松了解运动的原理
  12. zoj 2587 Unique Attack 最小割判定
  13. PAT乙级1034. 有理数四则运算(20)
  14. ZS and The Birthday Paradox
  15. 只用120行Java代码写一个自己的区块链
  16. mysql备份与还原 数据库的常用命令。
  17. GUI学习之七——单选框QRadioButton和QButtonGroup的学习总结
  18. centos 安装mysql Package: akonadi-mysql-1.9.2-4.el7.x86_64 (@anaconda)
  19. mybatis 的sql语句及使用mybatis的动态sql mybatis防注入
  20. 【1】【leetcode-130】 被围绕的区域

热门文章

  1. TapTap推广统计逻辑
  2. (转)解锁MySQL备份恢复的4种正确姿势
  3. Spring Boot 日志配置
  4. 分析org.rpgpoet.Music.wizards.length
  5. wordpress时间函数the_time() 详解
  6. C/C++ -- Gui编程 -- Qt库的使用 -- 使用图片与动画
  7. 可视化Echarts的使用示例
  8. 代码阅读——十个C开源项目
  9. (转)linux内核调优参数对比和解释
  10. URL编码分析与乱码解决方案