zookeeper模拟管理分布式服务器
首先看下使用场景:
首先有一个集群的服务器,然后我有客户端,我现在要去访问服务器,服务器那么多,我并不知道此时需要访问哪个服务器,
这个时候我们在这个中间搭建一个zookeeper集群。首先我的服务器每上一台就将这台服务器注册到zookeeper上。这里的注册也就是获取这个zookeeper的zkClient.让后将数据建在zookeeper维护的结点上。然后我的客户端,每次访问的时候都去获取zookeeper中当前某个节点(服务节点)下面的子节点,并且监听这个父节点。而服务器注册信息就是这个父结点下面的子结点。并且获取结点的同时注册监听。那么当某一台服务器上线,那么这个客户端因为处于监听状态,所以是可以实时更新。这个客户端也需要获取这个zkClient。然后获得结点注册监听。
我做了一个简单的例子:分别模拟服务器端注册信息,客户端进行监听。
首先我在的zookeeper集群搭建起来(具体搭建可以查看我之前的博客)
服务器代码:
package cn.itcast.bigdata.zkdist;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class DistributedServer {
// private static final String connectString = "mini1:2181,mini2:2181,mini3:2181";
private static final String connectString = "192.168.1.129:2181,192.168.1.130:2181,192.168.1.131:2181";
private static final int sessionTimeout = 2000;//超时时间
private static final String parentNode = "/servers";
ZooKeeper zk = null;
public void getConnect() throws Exception{
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
//收到时间通知后的回调函数(应该是我们自己的时间处理逻辑)
System.out.println(event.getType()+"---------"+event.getPath());
try {
zk.getChildren("/", true);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
public void registerServer(String hostname) throws Exception{
//创建的这个节点是一个临时的有序列的节点,那么当我这个服务器挡掉了,这个及节点就会消失,zk就相当于zkClient挡掉后,这个客户端创建的临时节点就会消失
String create = zk.create(parentNode+"/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname + "is online:.." + create);
}
public void handleBussiness(String hostname) throws InterruptedException{
System.out.println(hostname + "start working");
Thread.sleep(Long.MAX_VALUE);
}
//守护线程主方法退出后,就自动停止线程,也就是说我这个main方法如果线程一直开启,整个就会有
//但是如果不是守护线程,那么如果我这个主线程开启,而主线程里面还有子线程,那么如果子线程没有关闭
//我这个主线程是关闭不了。这样是不好的,一般都是主线程关闭就整个关闭了
//打包成jar包然后运行,这个main就是一个守护线程。然后service中的业务就没有必要另外开启Thread.sleep(Long.max)
public static void main(String[] args) throws Exception{
//获取zk连接
DistributedServer server = new DistributedServer();
server.getConnect();
//利用zk连接注册服务器信息,这个就是某一个业务服务器在zookeeper上注册信息
server.registerServer(args[0]);
//启动业务功能
server.handleBussiness(args[0]);
}
}
客户端代码:
package cn.itcast.bigdata.zkdist;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class DistributedClient {
// private static final String connectString = "mini1:2181,mini2:2181,mini3:2181";
private static final String connectString = "192.168.1.129:2181,192.168.1.130:2181,192.168.1.131:2181";
private static final int sessionTimeout = 2000;//超时时间
//加上这个volitile的作用是在于多线程访问的时候每个线程都会复制一份serverList进行操作,然后进行操作,
//可能其他线程已经对其进行修改,因为这个是一个全局变量,共享的,只是线程进来了会复制一份修改,然后在保存过去
//那我设置了volitile这个后,我的这个serverList就不能复制修改,只能直接修改,那么一个线程的修改其他线程就马上知道
private volatile List<String> serverList;
private static final String parentNode = "/servers";
ZooKeeper zk = null;
public void getConnect() throws Exception{
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
//收到时间通知后的回调函数(应该是我们自己的时间处理逻辑)
try {
//事件发生的时候这个方法就会发生,那么就重新更新服务器列表,并且注册监听
getServerList();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
public void getServerList() throws Exception{
//获取服务器子节点信息,并且对父节点进行监听
//我这个相当于客户端获取zookeeper连接,然后一直监听,然后只要zookeeper上有节点也就是服务器端有变动,
//我上面的这个监听器就能监听到然后调用这个方法,看service下面的节点,是否有挡掉
List<String> children = zk.getChildren(parentNode, true);//true说明获取节点的时候监听
List<String> servers = new ArrayList<String>();
for(String child:children){
byte[] data = zk.getData(parentNode + "/" + child, false, null);//这里false说明取节点数据的时候不需要监听
servers.add(new String(data));
}
//这个serverList就是共各个业务线程使用,我这个Client相当于客户访问的后台(这个后台是客户端),然后查看zookeeper,然后选择具体访问的是哪个服务器(真正服务的)。
serverList = servers;//这个mini1和mini2其实就是这个ip
//打印服务器列表
System.out.println(serverList);
}
public void handleBussiness() throws InterruptedException{
System.out.println("client start working");
Thread.sleep(Long.MAX_VALUE);
}
//守护线程主方法退出后,就自动停止线程,也就是说我这个main方法如果线程一直开启,整个就会有
//但是如果不是守护线程,那么如果我这个主线程开启,而主线程里面还有子线程,那么如果子线程没有关闭
//我这个主线程是关闭不了。这样是不好的,一般都是主线程关闭就整个关闭了
//打包成jar包然后运行,这个main就是一个守护线程。然后service中的业务就没有必要另外开启Thread.sleep(Long.max)
//那么我吧这server和client都打包成jar包,那么这个就不会自动关闭,智能手动关闭mian方法
public static void main(String[] args) throws Exception{
//获取zk连接
DistributedClient client = new DistributedClient();
client.getConnect();
//获取servers的子节点信息(并监听),从中获取服务器信息列表
client.getServerList();
//启动业务功能
client.handleBussiness();
}
}
然后我将这两个文件分别打成jar包,使用守护线程运行:
一下是运行结果:
首先我运行客户端:
zookeeper中什么都没有;
然后我运行一个服务器:
此时客户端:
当我运行三个服务器端客户端:
接着一个服务器挡掉了。客户端显示: