距离上一篇的更新也是好一段日子了,期间也是有小伙伴催更但是由于一些杂事一直没处理好,接下来会恢复周更java
有小伙伴的反馈中说到标题都是经典应用,太过笼统因此无法经过标题得知对应内容的问题,在此也是进行了改进node
第四篇:Zookeeper的经典应用场景---标题修改成---Zookeeper的分布式队列
第五篇:Zookeeper的经典应用场景2---标题修改成---Zookeeper的配置中心应用
复制代码
从零开始的高并发(一)--- Zookeeper的基础概念算法
从零开始的高并发(二)--- Zookeeper实现分布式锁编程
从零开始的高并发(三)--- Zookeeper集群的搭建和leader选举缓存
从零开始的高并发(四)--- Zookeeper的分布式队列session
从零开始的高并发(五)--- Zookeeper的配置中心应用架构
在分布式架构中常常采用的结构就是一主多从,主节点祈使句就是负责协调管理集群用的。咱们能够借用这个场景去展开。并发
为何咱们要采用临时节点,觉得考虑到master节点可能负载较高,挂掉了,这时咱们要保证其余下面的servers服务都可以获得通知,框架
其实咱们也能够经过最小节点方式来实现,就谁排在前面,谁就有权利去得到master运维
首先咱们须要有一个master节点,而后cluster表明的是集群的名字,name是指服务名,address是指服务的地址。masterPath是指master的znode目录,value是用来记录上面的name加address的,ZkClient不用多说了,从第二篇开始用这货用到如今了
private String cluster, name, address;
private final String masterPath, value;
private String master;
private ZkClient client;
复制代码
public Server(String cluster, String name, String address) {
super();
this.cluster = cluster;
this.name = name;
this.address = address;
masterPath = "/" + this.cluster + "/master";
value = "name:" + this.name + " address:" + this.address;
client = new ZkClient("localhost:2181");
client.setZkSerializer(new MyZkSerializer());
String serversPath = "/" + this.cluster + "/servers";
client.createPersistent(serversPath, true);
String serverPath = serversPath + "/" + name;
client.createEphemeral(serverPath, value);
new Thread(new Runnable() {
@Override
public void run() {
electionMaster(client);
}
}).start();
}
复制代码
进来时你要先告诉我,你是属于哪一个集群的啊,你的名字,还有你的对应地址是什么,而后咱们的masterPath就定义在这个集群的master目录下,这种方式让咱们方便记录多个不一样的集群。
serversPath是指集群中全部服务信息存放的znode根目录路径,serverPath是一个具体的服务,是刚刚的根目录+这个服务的名字,这个服务咱们是要存放于临时节点(createEphemeral()方法)上的,刚刚的根目录咱们是建立了持久节点(createPersistent()方法),为何咱们如今要使用临时节点了呢,由于咱们刚刚也提到了,master节点是能够挂掉的,并且咱们以后也会进行master节点挂掉的模拟,value是用来记录name和address的,在以后的执行结果中会打印出来
最后经过一个线程去执行这个master的选举,咱们在master的选举中要不停地监听咱们的master,在master被抢到了之后,我须要对这个线程进行阻塞操做,但是若是这时候主线程阻塞了,整个程序就不跑了,因此咱们必须经过一个子线程去帮咱们去抢master,此时就算我抢不到master,也能保证整个程序能够继续往下执行。
public void electionMaster(ZkClient client) {
try {
//尝试去获取master
client.createEphemeral(masterPath, value);
//若是成功,记录master节点信息
//在这里咱们记录master信息并做为缓存,方便得知这个集群的master是谁
//这样就能够再也不使用zookeeper去查master了
master = client.readData(masterPath);
System.out.println(value + "建立节点成功,成为Master");
} catch (ZkNodeExistsException e) {
//没有抢到master的状况下,咱们把如今master的信息读到本身的服务里面
master = client.readData(masterPath);
System.out.println("Master为:" + master);
}
// 为阻塞本身等待而用
CountDownLatch cdl = new CountDownLatch(1);
// 注册watcher
IZkDataListener listener = new IZkDataListener() {
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("-----监听到节点被删除");
cdl.countDown();
}
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
}
};
client.subscribeDataChanges(masterPath, listener);
// 让本身阻塞
if (client.exists(masterPath)) {
try {
cdl.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
// 醒来后,取消watcher
client.unsubscribeDataChanges(masterPath, listener);
// 递归调本身(下一次选举)
electionMaster(client);
}
public void close() {
client.close();
}
}
复制代码
咱们监听节点的删除事件,一旦master节点被释放掉了,咱们会当即唤醒本身的线程去参与争抢。在最后还进行了一个if (client.exists(masterPath))的判断,肯定是已经有节点成为了master了,咱们就会经过await方法让本身阻塞,若是不存在master,那就从新调用自身争抢master的方法
最后的close()方法是为了模拟机器挂掉而使用的,与代码自己的逻辑无关
咱们使用线程来代替进程,ScheduledThreadPoolExecutor是一个定时任务,5秒后我会关闭s1,10秒后关闭s2以此类推。
public static void main(String[] args) {
// 测试时,依次开启多个Server实例java进程,而后中止获取的master的节点,看谁抢到Master
Server s1 = new MasterElectionDemo().new Server("cluster1", "server1", "192.168.1.11:8991");
Server s2 = new MasterElectionDemo().new Server("cluster1", "server2", "192.168.1.11:8992");
Server s3 = new MasterElectionDemo().new Server("cluster1", "server3", "192.168.1.11:8993");
Server s4 = new MasterElectionDemo().new Server("cluster1", "server4", "192.168.1.11:8994");
ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(1);
scheduled.schedule(()->{
System.out.println("关闭s1");
s1.close();
}, 5, TimeUnit.SECONDS);
scheduled.schedule(()->{
System.out.println("关闭s2");
s2.close();
}, 10, TimeUnit.SECONDS);
scheduled.schedule(()->{
System.out.println("关闭s3");
s3.close();
}, 15, TimeUnit.SECONDS);
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
复制代码
由于咱们s1是第一个被建立的,因此它成为master的可能性固然是最大的
此时咱们5秒后关闭s1,这时监听到master节点没了,你们都被唤醒去抢master,而后s4抢到了,成为了master
此时咱们再删除s2和s3,就会发现和上面不同了,由于s4是master,因此我删除s2,或者s3都不要紧,都不像删除s1的时候会再触发master的选举,假设咱们下次不是s4抢到,而是s3抢到了master,那咱们在关闭s3的时候,就会像s1关闭时那样从新触发选举了,不过此时就仅仅还剩s4了而已
在本身仍是0基础的时候,如何来经过官网去学习,虽然如今网上关于各大框架的知识确实已经不少了,但是咱们仍是有必要去浏览这些开源框架的官网,由于不管网上的说法再良莠不齐众说纷纭,但官网是表明着绝对权威的
并且官网所提到的知识通常也是基础的应用,不会说涉及不少复杂的业务场景,从官网上去了解一个新的框架是最好的,也不会存在带有误导性的demo和总结,在大体了解以后,想看看具体的业务中使用,则可使用百度,去看看别人写的一些应用,就没有问题。
若是官网都会出错,那就只能本身看源码了,过程则会很是痛苦(摊手)
基本会按照以前写的章节的内容去大体说明,就是之前章节提到的内容在官网中哪里能够找到
这是zookeeper官网的目录结构,整个zookeeper的骨架都会列出来
这里其实就是介绍了每一个栏目分别都存在了什么东西,上面的图其实也大体翻译了这些内容
Developing Distributed Applications that use ZooKeeper
Introduction---这个模块的简介
The ZooKeeper Data Model---zookeeper的数据模型
ZNodes---znode的介绍
Watches---监听机制的介绍
Data Access---以前谈到额ACL
Ephemeral Nodes---临时节点的介绍
Sequence Nodes -- Unique Naming---顺序节点的介绍及命名惟一规则
这俩是3.5.3的新加入的节点类型,咱们使用的版本并未涉及
Container Nodes
TTL Nodes
Time in ZooKeeper---zookeeper的时间定义
ZooKeeper Stat Structure---节点的元数据,zxid那些
ZooKeeper Sessions---session相关
ZooKeeper Watches---watch想关及watch怎样用
Semantics of Watches---watch的意义
Remove Watches---移除
What ZooKeeper Guarantees about Watches---watch的机制
Things to Remember about Watches---使用注意的事项
如下也不一一翻译了,就是各个方面相关的知识呗
ZooKeeper access control using ACLs
ACL Permissions
Builtin ACL Schemes
ZooKeeper C client API
Pluggable ZooKeeper authentication
Consistency Guarantees---特性
Bindings---客户端使用方面的
Java Binding
Client Configuration Parameters
C Binding
Installation
Building Your Own C Client
Building Blocks: A Guide to ZooKeeper Operations
Handling Errors
Connecting to ZooKeeper
Read Operations
Write Operations
Handling Watches
Miscelleaneous ZooKeeper Operations
Program Structure, with Simple Example
Gotchas: Common Problems and Troubleshooting
复制代码
ZooKeeper Java Example
A Simple Watch Client
Requirements
Program Design
The Executor Class
The DataMonitor Class
Complete Source Listings
复制代码
在program design中会介绍这个程序所要达成的目的
这里提到了栅栏和队列的实现,不过在以前的文章中实现的方式和官网的不一样
Recipes上面的是一些更为高级的使用,双栅栏,权重队列,共享锁,对leader选举的补充等等
这里涉及到一些运维和管理员的使用文档,好比怎么去搭建集群等,在这里只贴部分,在以前的文章也有提到
dataLogDir也是经过log4j来进行管理的
JMX:zookeeper默认实现了JMX接口,内容也包括了以前咱们提到的jconsole
Observers Guide:观察者,其实它的职责就是服务集群,能够用来分担读请求的负载,还可让leader选举的时候更加快速,在集群数较多的时候,进行leader选举是一个很是耗时的过程,好比我只让几个节点来参与leader选举,其他的只是做为观察者,不参与投票而仅同步数据。
开票就提到了咱们说过的原子广播协议
wiki中有关于zab的介绍,包括paxos算法的介绍也有
curator的一种比较优雅的链式编程
curator自身实现了leader选举和分布式锁等功能,leader选举中Curator提供了LeaderSelector监听器实现Leader选举功能,同一时刻,只有一个Listener会进入takeLeadership()方法,说明它是当前的Leader。不过若是不清楚这个机制的话代码走起来讲真的其实有点懵。而分布式锁呢curator是经过一个InterProcessMutexDemo类来实现的,这里就不展开了。
到这篇为止zookeeper的东西就差很少了,也算是填了一个坑吧,代码从二到六也算是敲的很多,并且基本都是完整地贴出来了,有兴趣想跑一下验证的话直接ctrl+c/v便可。以后应该是按部就班,RPC再到dubbo的一个路线
下一篇:从零开始的高并发(七)--- RPC的介绍,协议及框架