1 eclipse环境配置
-
普通的java项目
-
创建一个java项目
-
依赖的jar包
zookeeper-3.4.7\lib下的 jline-0.9.94.jar log4j-1.2.15.jar netty-3.2.2.Final.jar slf4j-api-1.6.1.jar slf4j-log4j12-1.6.1.jar zookeeper-3.4.7\zookeeper-3.4.7.jar
-
-
maven项目
-
创建maven项目步骤:略
-
pom.xml (查询该坐标的网站:http://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper/3.4.7)
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.7</version> <type>pom</type> </dependency>
-
在maven工程的src/main/resource目录放置一个log4j.properties,其内容如下:
# Rules reminder: # DEBUG < INFO < WARN < ERROR < FATAL # Global logging configuration log4j.rootLogger=INFO,R,stdout ## File output... log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=/home/hadoop/flume/logs/test.log log4j.appender.R.MaxFileSize=100MB log4j.appender.R.MaxBackupIndex=7 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%d [%t] %-5p%l method\:%M %m%n #log4j.logger.org.apache.catalina=INFO,R,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p%l method\:%M %m%n #%d:显示日志记录时间 #[%t]:输出产生该日志事件的线程名 #%-5p:显示该条日志的优先级 #%l:输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 #%M:方法名 #%m:显示输出消息 #%n:当前平台下的换行符
-
2 基本操作
public class TestCRUD {
private static final String connectString = "min1:2181," +"min2:2181," +"min3:2181";
private static final int sessionTimeout = 2000;
@Test
public void testConnect(){
ZooKeeper zkClient = null;
try {
//连接成功后,会回调watcher监听,此连接操作是异步的,执行完new语句后,直接调用后续代码
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
/**
* public enum EventType {
None (-1),
NodeCreated (1),
NodeDeleted (2),
NodeDataChanged (3),
NodeChildrenChanged (4);
*/
@Override
public void process(WatchedEvent event) {
// 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
System.out.println("=========="+event.getType()+"===="+event.getState());
}
});
} catch (IOException e) {
e.printStackTrace();
}
//连接状态值 CONNECTING, ASSOCIATING, CONNECTED, CONNECTEDREADONLY,
System.out.println("-------------------"+zkClient.getState());
while(true){
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
//等待zookeeper客户端连接服务器成功的,查看连接时的状态信息。
System.out.println("====-----===="+zkClient.getState());
}
}
@Test
public void testCreate() throws Exception {
ZooKeeper zkClient = new ZooKeeper(connectString, sessionTimeout,
new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("=========="+event.getType());
}
});
Thread.sleep(10*1000);//等待的目的是使zookeeper客户端连接成功后,才执行后续操作
if(zkClient.getState() == States.CONNECTED){//连接服务器连接成功
String nodeCreated = zkClient.create("/age", //:要创建的节点的路径
"18".getBytes(), //节点中的数据
Ids.OPEN_ACL_UNSAFE, //节点的权限
CreateMode.PERSISTENT);//节点的类型
System.out.println("创建成功后,节点的真是路径是:"+nodeCreated);
}
}
//判断znode是否存在
@Test
public void testExist() throws Exception{
ZooKeeper zkClient = new ZooKeeper(connectString, sessionTimeout,
new Watcher() {
//修改目标节点 event.getType()的值是NodeDataChanged
@Override
public void process(WatchedEvent event) {
System.out.println("=========="+event.getType());
}
});
Thread.sleep(10*1000);//等待的目的是使zookeeper客户端连接成功后,才执行后续操作
//第二个参数表示 是否监听”/age“节点的状态的改变,为true时,则监听,当另一个zookeeper客户端修改、删除该节点的值时,回调watcher
Stat stat = zkClient.exists("/age", true);
System.out.println(stat==null?"not exist":"exist");
Thread.sleep(20*1000);
}
//获取znode的数据
@Test
public void getData() throws Exception {
ZooKeeper zkClient = new ZooKeeper(connectString, sessionTimeout,
new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("=========="+event.getType());
}
});
Thread.sleep(10*1000);//等待的目的是使zookeeper客户端连接成功后,才执行后续操作
byte[] data = zkClient.getData("/age", false, null);
System.out.println(new String(data));
}
// 获取子节点
@Test
public void getChildren() throws Exception {
ZooKeeper zkClient = new ZooKeeper(connectString, sessionTimeout,
new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("=========="+event.getType());
}
});
Thread.sleep(10*1000);//等待的目的是使zookeeper客户端连接成功后,才执行后续操作
//只能获得一个路径下的直接子节点的信息,不能递归获取孙节点
List<String> children = zkClient.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
Thread.sleep(Long.MAX_VALUE);
}
//删除znode
@Test
public void deleteZnode() throws Exception {
ZooKeeper zkClient = new ZooKeeper(connectString, sessionTimeout,
new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("=========="+event.getType());
}
});
Thread.sleep(10*1000);//等待的目的是使zookeeper客户端连接成功后,才执行后续操作
//参数2:指定要删除的版本,-1表示删除所有版本
zkClient.delete("/age", -1);
}
}
3 监听集群中主机的上下线
案例说明:
1.DistributedServer程序一运行起来就向zookeeper的/servers路径下创建一个临时节点
如该类启动三个实例时,分别为传入三个字符串,分别为 server1,server2,server3
根据代码逻辑,会在zookeeper的/servers下创建三个EPHEMERAL_SEQUENTIAL类型的节点,节点名为
/servers/server000001 此节点对应的value是server1
/servers/server000002 此节点对应的value是server2
/servers/server000003 此节点对应的value是server3
2.DistributedClient代码实时夺取/servers下的节点集合内容
如 [/servers/server000001,/servers/server000002,/servers/server000003]
说明这个客户端所在的机器,可用的集群有三台机器
DistributedClient代码监听了/servers目录的子节点,当有机器掉线时,/servers下的节点集合内容会变化的
如 [/servers/server000001,,/servers/server000003]
[/servers/server000003]
这样也就实时感知了三台集群中机器的状态
总结:有四台机器组成的集群,如a1,a2,a3,a4其中a1是主节点;a2,a3,a4是从节点
DistributedServer代码分别运行在a2,a3,a4从节点上
DistributedClient代码运行在a1主节点上
代码如下:
public class DistributedServer {
private static final String connectString = "min1:2181," +"min2:2181," +"min3:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/test2017-12-25";
private ZooKeeper zk = null;
public static void main(String[] args) throws Exception {
// 获取zk连接
DistributedServer server = new DistributedServer();
server.getConnect();
Thread.sleep(10*1000);
// 利用zk连接注册服务器信息
server.registerServer(args[0]);
// 启动业务功能
server.handleBussiness(args[0]);
}
/**
* 创建到zk的客户端连接
*/
public void getConnect() throws Exception {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
System.out.println(event.getType() + "---" + event.getPath());
try {
zk.getChildren("/", true);
} catch (Exception e) {
}
}
});
}
/**
* 向zk集群注册服务器信息
*/
public void registerServer(String hostname) throws Exception {
// CreateMode.EPHEMERAL_SEQUENTIAL 节点类型
/**
* 参数3 acl是Access Contral
* ZooKeeper使用ACL来控制访问其znode(ZooKeeper的数据树的数据节点)。ACL的实现方式非常类似于UNIX文件的访问权限:它采用访问权限位 允许/禁止 对节点的各种操作以及能进行操作的范围。
* 不同于UNIX权限的是,ZooKeeper的节点不局限于 用户(文件的拥有者),组和其他人(其它)这三个标准范围。ZooKeeper不具有znode的拥有者的概念。相反,ACL指定id集以及与之对应的权限。
* http://ifeve.com/zookeeper-access-control-using-acls/
*/
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);
}
}
public class DistributedClient {
private static final String connectString = "min1:2181," +"min2:2181," +"min3:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/test2017-12-25";
private volatile List<String> serverList;
private ZooKeeper zk = null;
public static void main(String[] args) throws Exception {
// 获取zk连接
DistributedClient client = new DistributedClient();
client.getConnect();
Thread.sleep(10*1000);
// 获取servers的子节点信息(并监听),从中获取服务器信息列表
client.getServerList();
// 业务线程启动
client.handleBussiness();
}
/**
* 创建到zk的客户端连接
*/
public void getConnect() throws Exception {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
try {
//重新更新服务器列表,并且注册了监听
getServerList();
} catch (Exception e) {}
}
});
}
/**
* 获取服务器信息列表
*/
public void getServerList() throws Exception {
// 获取服务器子节点信息,并且对父节点进行监听
List<String> children = zk.getChildren(parentNode, true);
// 先创建一个局部的list来存服务器信息
List<String> servers = new ArrayList<String>();
for (String child : children) {
// child只是子节点的节点名
byte[] data = zk.getData(parentNode + "/" + child, false, null);
servers.add(new String(data));
}
// 把servers赋值给成员变量serverList,已提供给各业务线程使用
serverList = servers;
//打印服务器列表
System.out.println(serverList);
}
/**
* 业务功能
*
* @throws InterruptedException
*/
public void handleBussiness() throws InterruptedException {
System.out.println("client start working.....");
Thread.sleep(Long.MAX_VALUE);
}
}