Hadoop之Zookeeper

来源:互联网 发布:linux上安装oracle11g 编辑:程序博客网 时间:2024/06/06 02:41

Hadoop之Zookeeper

ZooKeeper是一个分布式的,开放源码的分布式应用程序协同服务。是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件

简介

曾是Hadoop子项目,现为顶级项目ZooKeeper是协同服务ZooKeeper为分布式应用提供服务ZooKeeper支持Java和C语言

提供服务

配置维护名字服务分布式同步组服务等。

目标

封装复杂、易错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户

ZooKeeper流程

1.选举Leader2.同步数据3.选举Leader过程中算法有很多,但要达到的选举标准是一致的4.Leader要具有最高的zxid5.集群中大多数的机器得到响应并follow选出的Leader

存放数据的数据结构

通过/分隔开路径名每个路径代表一个节点Znode(zookeeper node)

子节点–Znode

每个Znode有自身信息,数据、长度、创建时间、修改时间。Znode维护数据、ACL(access control list,访问控制列表)、时间戳等交换版本号等数据结构,它通过对这些数据的管理来让缓存生效并且令协调更新。每当Znode中的数据更新后版本号将增加。
  • 读写操作

    读写数据原子性,读就读取所有数据,写入时完全覆盖。Znode的ACL存储用户操作权限。

  • 临时节点

    和session相关,session结束,节点删除

ZooKeeper安装

ZooKeeper 常用四字命令

ZooKeeper 支持某些特定的四字命令字母与其的交互。它们大多是查询命令,用来获取ZooKeeper 服务的当前状态及相关信息。用户在客户端可以通过 telnet 或 nc 向 ZooKeeper 提交相应的命令

1. 可以通过命令:echo stat|nc 127.0.0.1 2181 来查看哪个节点被选择作为follower或者leader2. 使用echo ruok|nc 127.0.0.1 2181 测试是否启动了该Server,若回复imok表示已经启动。3. echo dump| nc 127.0.0.1 2181 ,列出未经处理的会话和临时节点。4. echo kill | nc 127.0.0.1 2181 ,关掉server5. echo conf | nc 127.0.0.1 2181 ,输出相关服务配置的详细信息。6. echo cons | nc 127.0.0.1 2181 ,列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息。7. echo envi |nc 127.0.0.1 2181 ,输出关于服务环境的详细信息(区别于 conf 命令)。8. echo reqs | nc 127.0.0.1 2181 ,列出未经处理的请求。9. echo wchs | nc 127.0.0.1 2181 ,列出服务器 watch 的详细信息。10. echo wchc | nc 127.0.0.1 2181 ,通过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。11. echo wchp | nc 127.0.0.1 2181 ,通过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。

Zookeeper的简单操作

Zookeeper的shell操作

  • Zookeeper命令工具

启动Zookeeper服务之后,输入以下命令,连接到Zookeeper服务:

    zkCli.sh -server localhost:2181

执行结果如下所示:

[root@master ~]# zkCli.sh -server master:2181Connecting to master:21812017-07-18 03:00:34,503 [myid:] - INFO  [main:Environment@100] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT2017-07-18 03:00:34,508 [myid:] - INFO  [main:Environment@100] - Client environment:host.name=master2017-07-18 03:00:34,509 [myid:] - INFO  [main:Environment@100] - Client environment:java.version=1.8.0_1312017-07-18 03:00:34,514 [myid:] - INFO  [main:Environment@100] - Client environment:java.vendor=Oracle Corporation2017-07-18 03:00:34,514 [myid:] - INFO  [main:Environment@100] - Client environment:java.home=/usr/local/java/jre2017-07-18 03:00:34,514 [myid:] - INFO  [main:Environment@100] - Client environment:java.class.path=/usr/local/zookeeper/bin/../build/classes:/usr/local/zookeeper/bin/../build/lib/*.jar:/usr/local/zookeeper/bin/../lib/slf4j-log4j12-1.6.1.jar:/usr/local/zookeeper/bin/../lib/slf4j-api-1.6.1.jar:/usr/local/zookeeper/bin/../lib/netty-3.7.0.Final.jar:/usr/local/zookeeper/bin/../lib/log4j-1.2.16.jar:/usr/local/zookeeper/bin/../lib/jline-0.9.94.jar:/usr/local/zookeeper/bin/../zookeeper-3.4.6.jar:/usr/local/zookeeper/bin/../src/java/lib/*.jar:/usr/local/zookeeper/bin/../conf:2017-07-18 03:00:34,514 [myid:] - INFO  [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib2017-07-18 03:00:34,515 [myid:] - INFO  [main:Environment@100] - Client environment:java.io.tmpdir=/tmp2017-07-18 03:00:34,515 [myid:] - INFO  [main:Environment@100] - Client environment:java.compiler=<NA>2017-07-18 03:00:34,516 [myid:] - INFO  [main:Environment@100] - Client environment:os.name=Linux2017-07-18 03:00:34,516 [myid:] - INFO  [main:Environment@100] - Client environment:os.arch=amd642017-07-18 03:00:34,517 [myid:] - INFO  [main:Environment@100] - Client environment:os.version=2.6.32-696.el6.x86_642017-07-18 03:00:34,517 [myid:] - INFO  [main:Environment@100] - Client environment:user.name=root2017-07-18 03:00:34,517 [myid:] - INFO  [main:Environment@100] - Client environment:user.home=/root2017-07-18 03:00:34,517 [myid:] - INFO  [main:Environment@100] - Client environment:user.dir=/root2017-07-18 03:00:34,521 [myid:] - INFO  [main:ZooKeeper@438] - Initiating client connection, connectString=master:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@531d72caWelcome to ZooKeeper!2017-07-18 03:00:34,572 [myid:] - INFO  [main-SendThread(master:2181):ClientCnxn$SendThread@975] - Opening socket connection to server master/192.168.1.151:2181. Will not attempt to authenticate using SASL (unknown error)JLine support is enabled2017-07-18 03:00:34,770 [myid:] - INFO  [main-SendThread(master:2181):ClientCnxn$SendThread@852] - Socket connection established to master/192.168.1.151:2181, initiating session[zk: master:2181(CONNECTING) 0] 2017-07-18 03:00:34,856 [myid:] - INFO  [main-SendThread(master:2181):ClientCnxn$SendThread@1235] - Session establishment complete on server master/192.168.1.151:2181, sessionid = 0x5d51e6be550002, negotiated timeout = 30000WATCHER::WatchedEvent state:SyncConnected type:None path:null

 连接成功之后,系统会输出Zookeeper的相关环境及配置信息,并在屏幕输出“welcome to Zookeeper!”等信息。输入help之后,屏幕会输出可用的Zookeeper命令

[zk: master:2181(CONNECTED) 3] helpZooKeeper -server host:port cmd argsstat path [watch]set path data [version]ls path [watch]delquota [-n|-b] pathls2 path [watch]setAcl path aclsetquota -n|-b val pathhistory redo cmdnoprintwatches on|offdelete path [version]sync pathlistquota pathrmr pathget path [watch]create [-s] [-e] path data acladdauth scheme authquit getAcl pathclose connect host:port
  • 使用Zookeeper命令的简单操作步骤

(1) 使用ls命令查看当前Zookeeper中所包含的内容:ls /

[zk: master:2181(CONNECTED) 4] ls /[zookeeper, hbase][zk: master:2181(CONNECTED) 5]

(2) 创建一个新的Znode节点”zk”,以及和它相关字符,执行命令:create /zk myData

[zk: master:2181(CONNECTED) 5] create /zk myDataCreated /zk

(3) 使用ls命令来查看现在Zookeeper的中所包含的内容:ls /

[zk: master:2181(CONNECTED) 0] ls /[zk, zookeeper, hbase]

(4) 使用get命令来确认所创建的Znode是否包含创建的字符串,执行命令:get /zk

[zk: master:2181(CONNECTED) 1] get /zkmyDatacZxid = 0x100000055ctime = Tue Jul 18 03:16:47 CST 2017mZxid = 0x100000055mtime = Tue Jul 18 03:16:47 CST 2017pZxid = 0x100000055cversion = 0dataVersion = 0aclVersion = 0ephemeralOwner = 0x0dataLength = 6numChildren = 0

(5) 通过set命令来对zk所关联的字符串进行设置,执行命令:set /zk hello

[zk: master:2181(CONNECTED) 2] set /zk hellocZxid = 0x100000055ctime = Tue Jul 18 03:16:47 CST 2017mZxid = 0x100000058mtime = Tue Jul 18 03:19:33 CST 2017pZxid = 0x100000055cversion = 0dataVersion = 1aclVersion = 0ephemeralOwner = 0x0dataLength = 5numChildren = 0

(6) 使用get命令来查看,上次修改的内容,执行命令:get /zk

[zk: master:2181(CONNECTED) 3] get /zkhellocZxid = 0x100000055ctime = Tue Jul 18 03:16:47 CST 2017mZxid = 0x100000058mtime = Tue Jul 18 03:19:33 CST 2017pZxid = 0x100000055cversion = 0dataVersion = 1aclVersion = 0ephemeralOwner = 0x0dataLength = 5numChildren = 0

(7) 将刚才创建的Znode删除,执行命令:delete /zk

(8) 最后再次使用ls命令查看Zookeeper中的内容,执行命令:ls /

[zk: master:2181(CONNECTED) 5] ls /[zookeeper, hbase]

Zookeeper的api的简单使用

  • ZookeeperAPI简介
  • 所用jar包 zookeeper-3.4.6.jar,jline-0.9.94.jar,log4j-1.2.16.jar,netty-3.7.0.Final.jar,slf4j-api-1.6.1.jar,slf4j-log4j12-1.6.1.jar

Zookeeper API共包含五个包,分别为:

  (1)org.apache.zookeeper  (2)org.apache.zookeeper.data  (3)org.apache.zookeeper.server  (4)org.apache.zookeeper.server.quorum  (5)org.apache.zookeeper.server.upgrade

其中org.apache.zookeeper,包含Zookeeper类,他是编程时最常用的类文件。这个类是Zookeeper客户端的主要类文件。如果要使用Zookeeper服务,应用程序首先必须创建一个Zookeeper实例,这时就需要使用此类。一旦客户端和Zookeeper服务建立起了连接,Zookeeper系统将会给次连接会话分配一个ID值,并且客户端将会周期性的向服务器端发送心跳来维持会话连接。只要连接有效,客户端就可以使用Zookeeper API来做相应处理了。

Zookeeper类提供了如下图所示的几类主要方法

  • Zookeeper API的使用

简单状态查询,stat对象为放回状态对象

    public static void main(String[] args) throws IOException, KeeperException, InterruptedException {        String connectString = "master:2181,slave1:2181,slave2:2181";        /**         * 异步完成         * connectString 连接串         * sessionTimeout 超时时间         * watcher 不检查null         */        ZooKeeper zk = new ZooKeeper(connectString, 2000, null);        Stat stat = new Stat();        zk.getData("/", null, stat);    }

创建路径

    //创建路径    @Test    public void createPath() throws Exception{        ZooKeeper zk = new ZooKeeper(connectString, 2000, null);        String path ="/hello";        String data = "hello_data";        //Ids访问控制列表 控制权限        String retPath=zk.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);        System.out.println(retPath);    }

删除路径

    //删除路径    @Test    public void deletePath() throws Exception{        ZooKeeper zk = new ZooKeeper(connectString, 2000, null);        String path ="/hello";        //version要删除的版本号dataVersion,数据版本        zk.delete(path, 0);    }

设置数据

    // 设置数据 添加子节点只需向path路径下添加路径    @Test    public void setData() throws Exception {        ZooKeeper zk = new ZooKeeper(connectString, 2000, null);        String path = "/hello/hello_data";        Stat stat = zk.setData(path, "t1".getBytes(), 0);        System.out.println(stat.getVersion());    }

获取子节点

    @Test    public void getChildren() throws Exception {        ZooKeeper zk = new ZooKeeper(connectString, 2000, null);        List<String> list = zk.getChildren("/", null);        for (String string : list) {            System.out.println(string);        }    }

观察者

    // 观察者 zk发生变化时触发    @Test    public void testWatcher() throws Exception {        // 内部类        // zk事件激活是一次性触发        Watcher w = new Watcher() {            @Override            public void process(WatchedEvent event) {                // TODO Auto-generated method stub                System.out.println("有事情发生" + event.getType());            }        };        ZooKeeper zk = new ZooKeeper(connectString, 2000, w);        String path = "/hello";        zk.getData(path, w, null);        zk.setData(path, "t2".getBytes(), 1);        while (true) {            Thread.sleep(5000);        }    }

ZooKeeper示例

假设一组服务器,用于为客户端提供一些服务。我们希望每个客户端都能够能够找到其中一台服务器,使其能够使用这些服务,挑战之一就是维护这组服务器列表。这组服务器的成员列表明显不能存在网络中的单个节点上,因为如果那个节点发生故障,就意味着是整个系统的故障(我们希望这个列表有很高的可用性)。假设我们有了一个可靠的方法解决了这个成员列表的存储问题。如果其中一台服务器出现故障,我们仍然需要解决如何从服务器成员列表中将它删除的问题。某个进程需要负责删除故障服务器,但注意不能由故障服务器自己来完成,因为故障服务器已经不再运行。

我们所描述的不是一个被动的分布式数据结构,而是一个主动的、能够在某个外部事件发生时修改数据项状态的数据结构。ZooKeeper提供这种服务,所以让我们看看如何使用它来实现这种众所周知的组成员管理应用。

ZooKeeper中的组成员关系

理解ZooKeeper的一种方法就是将其看作一个具有高可用性的文件系统。但这个文件系统中没有文件和目录,而是统一使用“节点”(node)的概念,称为znode。znode既可以作为保存数据的容器(如同文件),也可以作为保存其他znode的容器(如同目录)。所有的znode构成一个层次化的命名空间。一种自然的建立组成员列表的方式就是利用这种层次结构,创建一个以组名为节点名的znode作为父节点,然后以组成员名(服务器名)为节点名来创建作为子节点的znode。如下图给出了一组具有层次结构的znode。

  • 创建组

    //为组名为/zoo的组创建一个znodepublic class CreateGroup implements Watcher {private static final String connectString = "master:2181";private static final String groupName = "zoo";private static final int SESSION_TIMEOUT = 5000;private ZooKeeper zk;// 使用Java的CountDownLatch类来阻止使用新建的ZooKeeper对象,直到这个ZooKeeper对象已经准备就绪private CountDownLatch connectedSignal = new CountDownLatch(1);/** * 客户端已经与ZooKeeper建立连接后,Watcher的process()方法会被调用 参数是一个表示该连接的事件 * SyncConnected连接事件 */@Overridepublic void process(WatchedEvent event) {    // TODO Auto-generated method stub    if (event.getState() == KeeperState.SyncConnected) {        /**         * 调用CountDownLatch的countDown()方法来递减它的计数器         * 锁存器(latch)被创建时带有一个值为1的计数器,用于表示在它释放所有等待线程之前需要发生的事件数。         * 在调用一欢countDown()方法之后,计数器的值变为0,则await()方法返回         */        connectedSignal.countDown();    }}public static void main(String[] args) throws IOException, InterruptedException, KeeperException {    /**     * connect方法实例化了一个新的ZooKeeper类的对象,这个类是客户端API中的主要类,     * 并且负责维护客户端和ZooKeeper服务之间的连接     *      */    CreateGroup createGroup = new CreateGroup();    createGroup.connect(connectString);    createGroup.create(groupName);    createGroup.close();}private void close() throws InterruptedException {    zk.close();}private void create(String groupName) throws KeeperException, InterruptedException {    String path = "/" + groupName;    if (zk.exists(path, false) == null) {        /**         * 使用ZooKeeper实例中的create()方法来创建一个新的ZooKeeper的znode 路径:用字符串表示。         * znode的内容:字节数组,本例中使用空值。         * 访问控制列表:简称ACL,本例中使用了完全开放的ACL,允许任何客户端对znode进行读写。         * 创建znode的类型:有两种类型的znode:短暂的和持久的。 PERSISTENT为持久         */        zk.create(path, null/* data */, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);    }    System.out.println("Created:" + path);}private void connect(String hosts) throws IOException, InterruptedException {    /**     * ZooKeeper类的构造函数有三个参数 第一个是:ZooKeeper服务的主机地址,可指定端口,默认端口是2181。     * 第二个是:以毫秒为单位的会话超时参数,这里我们设成5秒。 第三个是:参数是一个Watcher对象的实例。     * Watcher对象接收来自于ZooKeeper的回调,以获得各种事件的通知     * CreateGroup是一个Watcher对象,因此我们将它传递给ZooKeeper的构造函数     */    zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this);    connectedSignal.await();}}
  • ConnectionWatcher基类

    public class ConnectionWatcher implements Watcher {private static final int SESSION_TIMEOUT = 5000;protected ZooKeeper zk;CountDownLatch connectedSignal = new CountDownLatch(1);public void connect(String host) throws IOException, InterruptedException {    zk = new ZooKeeper(host, SESSION_TIMEOUT, this);    connectedSignal.await();}@Overridepublic void process(WatchedEvent event) {    // TODO Auto-generated method stub    if (event.getState() == KeeperState.SyncConnected) {        connectedSignal.countDown();    }}public void close() throws InterruptedException {    zk.close();}}
  • 加入组

    public class JoinGroup extends ConnectionWatcher {public void join(String groupName, String memberName) throws KeeperException, InterruptedException {    String path = "/" + groupName + "/" + memberName;    /**     * EPHEMERAL为短暂     * 创建短暂znode,作为组znode的子节点,然后通过休眠来模拟正在做某种工作,直到该进程被强行终止     * 随着进程终止,这个短暂znode被ZooKeeper删除     */    String createdPath = zk.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);    System.out.println("Created:" + createdPath);}private static final String connectString = "master:2181";private static final String groupName = "zoo";//private static final String memberName = "duck";//private static final String memberName = "cow";private static final String memberName = "goat";public static void main(String[] args) throws InterruptedException, IOException, KeeperException {    JoinGroup joinGroup = new JoinGroup();    joinGroup.connect(connectString);    joinGroup.join(groupName, memberName);    // stay alive until process is killed or thread is interrupted    Thread.sleep(Long.MAX_VALUE);}}
  • 显示成员列表

    public class ListGroup extends ConnectionWatcher {public void list(String groupNmae) throws KeeperException, InterruptedException {    String path = "/" + groupNmae;    try {        /**         * 调用了getChildren()方法来检索并打印输出一个znode的子节点列表         * 用参数为:该znode的路径和设为false的观察标志         */        List<String> children = zk.getChildren(path, false);        if (children.isEmpty()) {            System.out.printf("No memebers in group %s\n", groupNmae);            System.exit(1);        }        for (String child : children) {            System.out.println(child);        }    } catch (KeeperException.NoNodeException e) {        System.out.printf("Group %s does not exist \n", groupNmae);        System.exit(1);    }}private static final String connectString = "master:2181";private static final String groupName = "zoo";public static void main(String[] args) throws IOException, InterruptedException, KeeperException {    ListGroup listGroup = new ListGroup();    listGroup.connect(connectString);    listGroup.list(groupName);    listGroup.close();}}
  • 删除组

    public class DeleteGroup extends ConnectionWatcher {private static final String connectString = "master:2181";private static final String groupName = "zoo";public void delete(String groupName) throws InterruptedException, KeeperException {    String path = "/" + groupName;    List<String> children;    try {        children = zk.getChildren(path, false);        for (String child : children) {            zk.delete(path + "/" + child, -1);        }        zk.delete(path, -1);    } catch (KeeperException.NoNodeException e) {        System.out.printf("Group %s does not exist\n", groupName);        System.exit(1);    }}public static void main(String[] args) throws InterruptedException, IOException, KeeperException {    DeleteGroup deleteGroup = new DeleteGroup();    deleteGroup.connect(connectString);    deleteGroup.delete(groupName);    deleteGroup.close();}}
原创粉丝点击