服务端需要做的是:1.连接zk
2.创建一个持久的服务器目录节点servers
3.每启动一个服务器,就在servers目录下生成一个有序的临时的服务器节点目录,它的主机名和端口号通过参数传递,如果设置成临时的,那么当这台服务器宕机之后,这个目录就会消失。
4.之后操作业务,使用Thread.sleep(Long.MAX_VALUE)不要让进程马上结束
客户端需要做的是:1.通过getChild,得到servers目录下的服务器值有哪些,获取可用的服务器列表,在get的时候设置监听器为true,来调取连接zk的时候设置的监听器。
2. 为了得到正确的服务器数量,我们必须在连接zk的时候设置监听器,时刻监听servers目录 ,在外面定义一个 list 设置为 volatile类型 这样一旦list中的值改变,马上就能同步。在获取服务器的方法中定义一个临时的list,添加完可用服务器列表之后,再赋值给外面的list。需要进行同步的原因是获取服务器的方法和选择调用哪台服务器的方法会在两个线程中运行如果调用的服务器刚好在调用之后宕机,就不对了,所以为了减少这种影响,需要一个同步的list。一旦监视器检测到服务器的变化,马上就会重新调用获取服务器列表的方法。而外面的list也会被重新赋值。此时调用服务器的方法中的list就会马上同步。
之后执行的时候我们可以打成jar包,使用java -jar 包名 参数
启动几个服务端,启动几个客户端,然后我们可以尝试关闭一个服务端来查看客户端输出信息的变化。我们会发现,当我们关闭一个服务端之后,需要等待两秒才会看到客户端输出的变化信息,这是因为zokkeeper的心跳机制,我们在连接zookeeper的时候设置的心跳就是两秒,所以只有两秒之后没有收到这台机器的信息才会认为这台机器失联。
以下贴出代码:
package com.test.mydistservers;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class PayServiceServer {
ZooKeeper zk = null;
public void connectZK() throws IOException{
String hosts = "marshal:2181,marshal01:2181,marshal02:2181,marshal03:2181,marshal04:2181,marshal05:2181";
zk = new ZooKeeper(hosts, 2000, null);
System.out.println("进去zk");
}
public void service() throws InterruptedException{
System.out.println("服务器开始接受业务请求");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
//获取zk客户端
PayServiceServer s = new PayServiceServer();
s.connectZK();
//注册信息
Stat exists = s.zk.exists("/servers", false);
if(exists==null){
s.zk.create("/servers", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
s.zk.create("/servers/server", args[0].getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
//处理业务
s.service();
}
}
package com.test.mydistservers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
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.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
public class PayServiceConsumer {
ZooKeeper zk = null;
//连接zk
public void ConnZK() throws IOException{
String hosts = "marshal:2181,marshal01:2181,marshal02:2181,marshal03:2181,marshal04:2181,marshal05:2181";
zk = new ZooKeeper(hosts, 2000, new Watcher(){
@Override
public void process(WatchedEvent event) {
//因为zookeeper一连接就会有一个空事件(KeeperState.SyncConnected)。所以需要先判断一下类型
if(event.getState()==KeeperState.SyncConnected&&event.getType()==EventType.NodeChildrenChanged){
try {
getServer();
} catch (KeeperException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
}
//用于存储可用服务器的列表
private volatile List<String> list = new ArrayList<String>();
//查询可用服务器
public void getServer() throws KeeperException, InterruptedException{
List<String> list2 = new ArrayList<String>();
List<String> children = zk.getChildren("/servers", true);
for (String string : children) {
byte[] data = zk.getData("/servers/"+string, false, null);
String string2 = new String(data);
System.out.println("可用的服务器有:"+string2);
list2.add(string2);
}
list = list2;
}
public void service() throws InterruptedException{
System.out.println("正在挑选服务器");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
//获取zk连接
PayServiceConsumer p = new PayServiceConsumer();
p.ConnZK();
//去zk上查询可用服务器
p.getServer();
p.service();
}
}