1、概念
zookeeper是一个分布式协调服务,就是为用户的分布式应用程序提供协调服务。
1>zookeeper是为别的分布式程序服务的。
2>zookeeper本身就是一个分布式程序(只要有半数以上的节点存活,zookeeper就能够正常服务)
3>zookeeper所提供的服务涵盖:主从协调、服务器节点动态上下线、统一配置管理、分布式共享锁、统一名称服务.....
4>虽然说可以提供各种服务,但是zookeeper在底层其实只提供了两个功能:管理(存储、读取)用户提交的数据;并为数据提供监听服务。
ZooKeeper的可靠性极高,其内部就是一个集群。
ZooKeeper集群中的角色:leader 和follower.。
只要集群中有半数以上的节点存活,集群就能提供服务。
ZooKeeper的集群机制:半数机制——集群中半数以上机器存活,集群就可用。ZooKeeper适合安装在奇数台机器上。
2、环境要求
需要首先安装好JDK,并配置环境变量,然后执行source /etc/profile
3、其他相关信息
ZooKeeper使用的是选举主从算法,投票选举机制,自动选主从,不需要配置。
ZooKeeper不适合用于数据更新频繁的场景。
在奇数台服务器上配置ZooKeeper,只需要配置 ID号,以及相应的服务器信息。
4、ZooKeeper的安装
1>将相应的安装包进行上传至Centos虚拟机中,进行解压缩处理后,进入相应的文件夹中。
2>文件夹中的有用的文件主要是bin、conf、lib、zookeeper-3.4.5.jar,由于src文件夹中存储的是其源码的Java文件,可以删除,也可以不删除。
3>修改配置文件:进入目录conf中,会有3个文件:configuration.xsl、log4j.properties、 zoo_sample.cfg
#:cp zoo_sample.cfg zoo.cfg
然后修改zoo.cfg文件:
vim zoo.cfg
{
其中有一行:datadir=/tmp/zookeeper 这是数据目录
将其修改为:datadir=/root/zkdata 因为/tmp文件夹中存储的是临时数据,不能永久 保存。
在文件最后填写部分内容:
例如:server.1=hadoop1(主机名):2888:3888
server.2=hadoop2:2888:3888
server.3(这个数字的表示myID需要将其记录到数据文件datadir所指的文件中)=hadoop3:2888:3888
}
4> 创建/root/zkdata,在其下创建一个文件,名称为myid,并在其中输入本机的id:
例如{echo 1>myid}
5> 然后使用scp命令将包含ZooKeeper相关文件的目录拷贝到其他主机上
例如: scp -r apps/ root@hadoop2:/root
6> 之后在主机hadoop2上创建/root/zkdata目录,然后在该目录下进行下面的操作
{ echo 2 > myid}
7> 然后同理在复制一个给hadoop3主机上。
在这三台主机上需要关闭防火墙:service iptables stop
8> 之后,便是启动zookeeper服务。
进入ZooKeeper相应的目录,然后执行#bin/zkServer.sh start 启动该服务
9> 查看哪一台机器是leader,哪几台机器是follower
执行#bin/zkServer.sh status
ZooKeeper的命令行客户端:
启动它: #bin/zkCli.sh
然后它会连接zookeeper服务器,它会首先连接本机的zookeeper服务端。
启动让它连接到其它主机的客户端:先执行#bin/zkCli.sh 然后执行#connect hadoop2:2181
客户端如何使用可以借助help命令查看。
5、ZooKeeper的数据结构
1>层次化的目录结构,命名符合常规文件系统规范。
2>每个节点在ZooKeeper中叫做znode,并且其有一个唯一的路径标识。
3>节点znode可以包含数据和子节点(但是EPHEMERAL类型的节点不能有子节点)
4>客户端应用可以在节点上设置监视器。
6、在ZooKeeper的命令行客户端下创建一个znode:
例如: create /app1 "this is a app1 servers parent"
ls /
create /app1/server01 "192.168.150.132"
获取节点中的数据:使用get 命令
例如: get /app1
进行数据的更新:
例如:set /app1 aaaaa
get /app1
监听功能:【watch】
例如:get /app1 watch 说明客户端能收到监听通知
7、ZooKeeper的节点类型(默认是持久类型的):
1》短暂(ephemeral):断开连接之后自己删除
持久(persistent):断开连接之后不删除
2》znode有四种形式的目录节点(默认是persistent)
PERSISTENT
PERSISTENT_SEQUENTIAL(持久序列)
EPHEMERAL
EPHEMERAL_SEQUENTIAL(临时序列)
3》创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器, 有父节点维护。
4》在分布式系统中,顺序号可以被用于为所有的事件进行全部排序,这样客户端可以通过顺序号推断事件的顺序。
8、其它
关于分布式应用系统服务器上下线动态感知程序开发:需求是客户端可以实时感知到服务器的上下线变化。
通过架设ZooKeeper集群当服务器启动时到ZooKeeper服务器集群上进行注册,ZooKeeper创建短暂(临时)节点。然后客户端去ZooKeeper集群上getchildren,获取到当前服务器列表(并且注册监听)。
分布式应用系统程序的例子如下所示:
编写的DistributedServer.java
{
package Hadoop;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
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 int sessionTimeout = 2000;
private static final String parentNode = "/servers";
private ZooKeeper zk = null;
/**
* 创建到zk的客户端连接
* */
public void getConnect() throws Exception
{
zk = new ZooKooper(connectString,sessionTimeout,new Watcher(){
public void process(WatchedEvent event)
{
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
System.out.println(event.getType()+"------"+event.getPath());
try{
zk.getChildren("/",true); //因为监听器对事件监听只起一次作用,所以要循环设置监听器,这里的true表示设定了监听;
}catch (Exception e)
{
e.printStackTrace();
}
}
});
}
/**向zk集群注册服务器信息*/
public void registerServer(String hostname) throws Exception
{
String create = zk.create(parentNode+"/server",hostname.getBytes(),Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTS);
System.out.println(hostname+"is online..."+create);
}
/**
* 业务功能
* */
public void handleBusiness(String hostname)
{
System.out.println(hostname+"start working....");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception
{
//获取zk连接
DistributedServer server = new DistributedServer();
server.getConnect();
//利用zk连接注册服务器信息
server.registerServer(args[0]);
//启动业务功能
server.handleBusiness(args[0]);
}
}
}
编写的DistributedClient.java
{
package Hadoop;
import org.omg.PortableInterceptor.DISCARDING;
import java.util.ArrayList;
import java.util.List;
public class DistributedClient
{
private static final String connectString="mini1:2181,mini2:2181,mini3:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/servers";
//注意:加volatile的意义何在?
private volatile List<String> serverList;
private ZooKeeper zk = null;
/**
* 创建到zk的客户端连接
* */
public void getConnect() throws Exception
{
zk = new ZooKooper(connectString,sessionTimeout,new Watcher(){
public void process(WatchedEvent event)
{
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
try{
//重新注册了服务器列表,并且注册了监听;
getServerList();
}catch (Exception e)
{
e.printStackTrace();
}
}
});
}
//获取服务器信息列表
public void getServerList() throws Exception
{
//获取服务器子节点信息,并且对父节点进行监听;
List<String> children = zk.getChildren(parentNode,true); //true表示有监听;
//先创建一个局部的list来存服务器信息
ArrayList<String> servers = new ArrayList<>();
for (String child:children)
{
//child只是子节点的节点名
byte[] data = zk.getData(parentNode+"/"+child,false,null);
servers.add(new String(data)); //将服务器的名字存到servers集合中;
}
//把servers赋值给成员变量serverList,以提供给各个业务线程使用;
serverList = servers;
//打印一下服务器列表
System.out.println(serverList);
}
/**
* 业务功能
* */
public void handleBusiness() throws InterruptedException
{
System.out.println("client start working....");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args)
{
//获取zk连接
DistributedClient client = new DistributedClient();
client.getConnect();
//获取server的子节点信息(并监听),从中获取服务器信息列表
client.getServerList();
//业务线程启动
client.handleBusiness();
}
}
}