简介
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、名字服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
安装和配置
zookeeper可以部署成单机模式,即服务端只有一个;也可部署成多机模式,可解决单点故障等问题。
单机模式
1. 从官网http://zookeeper.apache.org下载zookeeper,这里我下载的是3.4.6,下载之后解压。
2. 进入解压的文件夹,在cfg目录下,将文件zoo_sample.cfg文件改为zoo.cfg。打开该文件,文件中的选项及相应的含义如下:
<span style="font-size:14px;"># The number of milliseconds of each tick
# zookeeper中使用的基本时间单位, 毫秒值.
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
# zookeeper集群中的包含多台server, 其中一台为leader, 集群中其余的server为follower.
# initLimit参数配置初始化连接时, follower和leader之间的最长心跳时间. 此时该参数设置为10,
# 说明时间限制为10倍tickTime, 即10*2000=20000ms=20s.
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
# 该参数配置leader和follower之间发送消息, 请求和应答的最大时间长度.
# 此时该参数设置为5, 说明时间限制为5倍tickTime, 即10000ms.
syncLimit=5
# the directory where the snapshot is stored.
# 存储内存中数据库快照的位置;
# 注意 应该谨慎地选择日志存放的位置,使用专用的日志存储设备能够大大地提高系统的性能,
# 如果将日志存储在比较繁忙的存储设备上,那么将会在很大程度上影响系统的性能
dataDir=/zookeeper/zookeeper-cluster/user/zookeeper1/data
# the port at which the clients will connect
# 监听客户端连接的端口
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
# 限制连接到 ZooKeeper 的客户端的数量,限制并发连接的数量,它通过 IP 来区分不同的客户端。
# 此配置选项可以用来阻止某些类别的 Dos 攻击。将它设置为 0 或者忽略而不进行设置将会取消对并发连接的限制。
maxClientCnxns=60
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
# 把事务日志写入到“ dataLogDir ”所指定的目录。这将允许使用一个专用的日志设备并且帮助我们避免日志和快照之间的竞争
# 该目录要手工建立,如果启用了该配置却没有建立相应的目录的话,启动后会报配置异常错误
dataLogDir=/zookeeper/zookeeper-cluster/user/zookeeper1/log</span>
3. 运行bin目录下的命令:zkServer.cmd即可部署成功zookeeper。
多机模式
1. 多机模式需要将刚才解压的文件在复制两份,然后在各自的zoo.cfg文件中添加配置:
<span style="font-size:14px;">server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889</span>
这三个配置代表了3个zookeeper服务,server.X=A:B:C 其中X是一个数字, 表示这是第几号server. A是该server所在的IP地址. B配置该server和集群中的leader交换消息所使用的端口. C配置选举leader时所使用的端口. 由于配置的是伪集群模式(在同一台机器上部署多个zookeeper服务), 所以各个server的B, C参数必须不同。
2. 然后在各自的data目录下添加文件:myid,在该文件中添加所代表服务的id号,即server.x中的x。
3.最后在各自的bin目录下执行命令:zkServer.cmd即可部署3个zookeeper服务。
客户端
Student类,zookeeper节点存储的业务对象
<span style="font-size:14px;">/**
* <pre>
* 项目名: common-zookeeper-demo1
* 类名: Student.java
* 类描述:
* </pre>
*/
public class Student implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4047971472299119914L;
private String id;
private String name;
private String age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public Student(String id, String name, String age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public Student() {
super();
}
}</span>
CommonUtils类,将业务对象序列化和反序列化的工具类:
<span style="font-size:14px;">/**
* <pre>
* 项目名: common-zookeeper-demo1
* 类名: CommonUtils.java
* 类描述: 工具类
* </pre>
*/
public class CommonUtils {
/**
* 将对象转化成byte数组
* @param object
* @return
* @throws IOException
*/
public static byte[] objectToBytes(Object object) throws IOException{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(object);
return byteArrayOutputStream.toByteArray();
}
/**
* 将byte数组转化成java对象
* @param bytes
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Object bytesToObject(byte[] bytes) throws IOException, ClassNotFoundException{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
}</span>
<span style="font-family:Comic Sans MS;font-size:14px;">MultiWatcher类,zookeeper节点监控类</span>
<pre name="code" class="java"><span style="font-size:14px;">/**
* 集群节点状态监控类
*
*/
public class MultiWatcher implements Watcher {
private Logger log = Logger.getLogger(MultiWatcher.class);
public MultiWatcher(String serverName) {
this.serverName = serverName;
}
private String serverName = null;
@Override
public void process(WatchedEvent event) {
log.info("处理服务器<" + serverName+">上发生的事件" +",事件明细信息:"+ event);
}
}</span>
测试类1:连接zookeeper,创建节点,设置数据,删除节点。
<pre name="code" class="java"><span style="font-size:14px;">/**
*
* <pre>
* 项目名: common-zookeeper-demo1
* 类名: ZooKeeperTest.java
* 类描述:
* </pre>
*/
public class ZooKeeperTest1 {
private static Logger log = Logger.getLogger(ZooKeeperTest1.class);
public static void main(String[] args) throws Exception {
//选择连接哪个zookeeper服务器,这里选择的是:ip为127.0.0.1,端口为:2181的zookeeper服务
ZooKeeper zk = new ZooKeeper("127.0.0.1:2181", 3000, new MultiWatcher("127.0.0.1:2181"));
Student s1 = new Student("1", "洪七公", "22");
if (zk.exists("/test/app1", false) != null) {
zk.delete("/test/app1", -1);
}
if (zk.exists("/test/app2", false) != null) {
zk.delete("/test/app2", -1);
}
if (zk.exists("/test", false) != null) {
zk.delete("/test", -1);
}
//在选择的zookeeper服务中创建节点
zk.create("/test", CommonUtils.objectToBytes(s1), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
Student s2 = (Student) CommonUtils.bytesToObject(zk.getData("/test", true, null));
log.info(s2);
s2.setName("欧阳锋");
zk.setData("/test", CommonUtils.objectToBytes(s2), -1);
log.info(CommonUtils.bytesToObject(zk.getData("/test", true, null)));
Student s3 = new Student("3", "陆小凤", "28");
/**
* 在节点/test下创建子节点app1,并放入数据
*/
zk.create("/test/app1", CommonUtils.objectToBytes(s3), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
Student s4 = new Student("4", "花满楼", "25");
/**
* 在节点/test下创建节点app2,并放入数据
*/
zk.create("/test/app2", CommonUtils.objectToBytes(s4), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
List<String> childs = zk.getChildren("/test", true);
if(null != childs && childs.size() > 0){
for(String childNode : childs){
Student stemp = (Student)CommonUtils.bytesToObject(zk.getData("/test/"+childNode, true , null));
log.info("节点 "+childNode+":"+(stemp));
}
}
System.in.read();
}
}</span>