大数据实训笔记6:zookeeper

概述

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

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

ZooKeeper包含一个简单的原语集,提供Java和C的接口。

ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在$zookeeper_home\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。

提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。

特点

 Zookeeper是由一个leader,多个follower组成的集群。

  • Leader是由follower选举产生,Leader负载投票发起和决议,更新系统状态。
  • Follower用于接收客户端的请求并返回结果,在leader的选举中参与投票。
  • 集群中只有半数以上的机器存活,集群才可以使用。
  • 整合集群中数据一致,每个server会保存一个副本,无论客户端连接到哪个server,数据都是一致的。
  • 更新请求是按顺序执行。
  • 保证数据的原子性,一次更新成功要么是成功,要么是失败。
  • 数据的实时性,客户端在更新数据时,在一定的时间范围内,数据是一致的。

安装部署

将下载好的Zookeeper安装包上传到hadoop101 /opt/software/ 并解压。

//解压
[hadoop@hadoop101 software]$ tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /opt/module/
//重命名
[hadoop@hadoop101 software]$ cd /opt/module/
[hadoop@hadoop101 module]$ mv apache-zookeeper-3.5.7-bin/ zookeeper-3.5.7/
//创建zkData文件夹
[hadoop@hadoop101 module]$ cd zookeeper-3.5.7
[hadoop@hadoop101 zookeeper-3.5.7]$ mkdir zkData
//在zkData文件夹中创建myid文件
[hadoop@hadoop101 zookeeper-3.5.7]$ cd zkData
[hadoop@hadoop101 zkData]$ vim myid

myid文件中,hadoop101的内容为1,hadoop102的内容为2,以此类推。 只要三台客户机的id不一样即可。

//重命名
[hadoop@hadoop101 zkData]$ cd ..
[hadoop@hadoop101 zookeeper-3.5.7]$ cd conf/
[hadoop@hadoop101 conf]$ mv zoo_sample.cfg zoo.cfg
//编辑
[hadoop@hadoop101 conf]$ vim zoo.cfg

找到dataDir,修改路径为我们刚刚创建的zkData文件夹。zookeeper集群中server.x的x为myid中的id,后面为对应的主机名。 

dataDir=/opt/module/zookeeper-3.5.7/zkData
###############zookeeper集群#################
server.1=hadoop101:2888:3888
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:2888
//分发
[hadoop@hadoop101 conf]$ cd /opt/module/
[hadoop@hadoop101 module]$ xsync zookeeper-3.5.7/

分发成功后,到hadoop102和hadoop103中修改myid。

集群的启动和停止

在三台客户机下分别执行下列命令。

//启动
[hadoop@hadoop101 zookeeper-3.5.7]$ bin/zkServer.sh start
//查看状态
[hadoop@hadoop101 zookeeper-3.5.7]$ bin/zkServer.sh status
//停止
[hadoop@hadoop101 zookeeper-3.5.7]$ bin/zkServer.sh stop

查看状态,我们可以看到,hadoop101和hadoop103都是follower,hadoop102是leader。这是因为,在本例中,超过半数为2。hadoop101启动时,试图成为leader,但其他两台客户机未响应,没有达到超过半数的要求。hadoop102启动时,试图成为leader,由于它的id比hadoop101大,因此101与102两台客户机都支持102成为leader,超过半数,102被选举为leader。hadoop103启动时,虽然它的id最大,但102已经成为leader,它只能成为follower。

像hadoop集群的启动和停止那样,在每台客户机中操作太繁琐。我们可以编写脚本zk.sh,方便集群的启动、停止和查看状态。

[hadoop@hadoop101 zookeeper-3.5.7]$ cd ~
[hadoop@hadoop101 ~]$ cd bin/
[hadoop@hadoop101 bin]$ sudo vim zk.sh
#!/bin/bash
case $1 in
"start"){
   for i in hadoop101 hadoop102 hadoop103
   do
     echo "****************$i*************"
     ssh $i "source /etc/profile;/opt/module/zookeeper-3.5.7/bin/zkServer.sh start"
   done
};;
"stop"){
   for i in hadoop101 hadoop102 hadoop103
   do
     echo "****************$i*************"
     ssh $i "source /etc/profile;/opt/module/zookeeper-3.5.7/bin/zkServer.sh stop"
   done
};;
"status"){
   for i in hadoop101 hadoop102 hadoop103
   do
     echo "****************$i*************"
     ssh $i "source /etc/profile;/opt/module/zookeeper-3.5.7/bin/zkServer.sh status"
   done
};;
esac
//修改权限
[hadoop@hadoop101 bin]$ sudo chmod 777 zk.sh
[hadoop@hadoop101 bin]$ cd ..
//分发
[hadoop@hadoop101 ~]$ xsync bin/
//启动
[hadoop@hadoop101 ~]$ zk.sh start
//查看状态
[hadoop@hadoop101 ~]$ zk.sh status
//停止
[hadoop@hadoop101 ~]$ zk.sh stop

客户端操作

客户端命令

在启动客户端之前要保证集群正常启动

//启动客户端
[hadoop@hadoop101 zookeeper-3.5.7]$ bin/zkCli.sh
//显示所有操作命令
[zk: localhost:2181(CONNECTED) 0] help
//查看当前znode中所包含的内容
[zk: localhost:2181(CONNECTED) 1] ls /
//查看当前节点详细数据
[zk: localhost:2181(CONNECTED) 2] ls2 /
//创建两个普通节点
[zk: localhost:2181(CONNECTED) 3] create /mydir "mydir"
[zk: localhost:2181(CONNECTED) 4] ls /
[zk: localhost:2181(CONNECTED) 5] create /mydir/cs "cs"
[zk: localhost:2181(CONNECTED) 6] ls /mydir
//获取节点
[zk: localhost:2181(CONNECTED) 7] get /mydir
[zk: localhost:2181(CONNECTED) 8] get /mydir/cs
//创建短暂节点
[zk: localhost:2181(CONNECTED) 9] create -e /mydir/temp "temp"
//这时候查看应该还是有节点temp的
[zk: localhost:2181(CONNECTED) 10] ls /mydir
//退出
[zk: localhost:2181(CONNECTED) 11] quit
//启动客户端
[hadoop@hadoop101 zookeeper-3.5.7]$ bin/zkCli.sh
//查看,短暂节点temp已经删除
[zk: localhost:2181(CONNECTED) 0] ls /mydir
//创建带序号的节点
[zk: localhost:2181(CONNECTED) 1] create -s /mydir/software "software"
[zk: localhost:2181(CONNECTED) 2] create -s /mydir/software "software2"
[zk: localhost:2181(CONNECTED) 3] ls /mydir
//在hadoop103中增加监听
[zk: localhost:2181(CONNECTED) 0] get /mydir watch
//在hadoop102中修改节点信息
[zk: localhost:2181(CONNECTED) 1] set /mydir "mydir1"

在hadoop103中,会弹出如下提示,说明监听成功。 

//在hadoop101中
//查看节点状态
[zk: localhost:2181(CONNECTED) 4] stat /mydir
//删除节点
[zk: localhost:2181(CONNECTED) 5] delete /mydir/cs
//递归删除节点
[zk: localhost:2181(CONNECTED) 6] deleteall /mydir

API应用

创建zookeeper_demo的maven工程,并导入pom.xml依赖。

<!-- 自定义属性设置版本号 -->
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <hadoop-version>2.7.2</hadoop-version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.10</version>
        </dependency>
    </dependencies>

创建log4j.properties日志,配置如下:

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

编写代码如下:

public class TestZookeeper {
    private Logger logger = Logger.getLogger(this.getClass());
    //中间不能有空格!!!
    private String connect = "hadoop101:2181,hadoop102:2181,hadoop103:2181";
    private int sessionTimeout = 2000; //超时
    private ZooKeeper zkClient;

    @Before
    public void init() throws IOException {
        //创建客户端对象
        zkClient = new ZooKeeper(connect, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                logger.info("------------启动-----------");
                try {
                    List<String> children = zkClient.getChildren("/", true);
                    for(String child: children) {
                        logger.info(child);
                    }
                    logger.info("------------停止-----------");
                } catch (KeeperException | InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    //创建永久节点
    @Test
    public void createNode() throws KeeperException, InterruptedException {
        String path = zkClient.create("/sanguo", "sg".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        logger.info(path);
    }

    //获取子节点变化
    @Test
    public void getChileNode() throws KeeperException, InterruptedException {
        List<String> children = zkClient.getChildren("/", true);
        for(String child: children) {
            logger.info(child);
        }
        //写一个线程,2s获取一次子节点的变化
        Thread.sleep(2000);
    }
}

运行后,到集群中查看,可以看到我们创建的sanguo节点。

输出日志如下:

案例: 监听节点上下线

需求:分布式系统中,主节点可以有多台,可以动态上下线,要求任意一台客户端都能事实感知到主节点服务器的上下线。

代码实现

//服务端
public class DistributeServer {
    private Logger logger = Logger.getLogger(this.getClass());
    private String connect = "hadoop101:2181,hadoop102:2181,hadoop103:2181";
    private int sessionTimeout = 2000; //超时
    private ZooKeeper zkClient;
    public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
        //创建对象
        DistributeServer server = new DistributeServer();
        //连接
        server.getConnect();
        //注册临时节点
        server.register(args[0]);
        //业务逻辑处理
        server.business();
    }

    private void getConnect() throws IOException {
        zkClient = new ZooKeeper(connect, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {

            }
        });
    }

    private void register(String hostname) throws KeeperException, InterruptedException {
        String host = zkClient.create("/servers/"+hostname, hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        logger.info(host + "上线");
    }

    private void business() throws InterruptedException {
        Thread.sleep(Long.MAX_VALUE);
    }
}
//客户端
public class DistributeClient {
    private Logger logger = Logger.getLogger(this.getClass());
    private String connect = "hadoop101:2181,hadoop102:2181,hadoop103:2181";
    private int sessionTimeout = 2000; //超时
    private ZooKeeper zkClient;

    public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
        //创建对象
        DistributeClient client = new DistributeClient();
        //连接
        client.getConnect();
        //获取子节点信息
        client.getChildrenNode();
        //业务逻辑处理
        client.business();
    }

    private void getConnect() throws IOException {
        zkClient = new ZooKeeper(connect, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                try {
                    getChildrenNode();
                } catch (KeeperException | InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void getChildrenNode() throws KeeperException, InterruptedException {
        List<String> children = zkClient.getChildren("/servers", true);
        List<String> hosts = new ArrayList<>();
        for(String child: children) {
            //获取servers数据
            byte[] data = zkClient.getData("/servers/"+child, false, null);
            hosts.add(new String(data));
        }
        logger.info(hosts);
    }

    private void business() throws InterruptedException {
        //休眠设置成最大值,否则一下就结束程序了
        Thread.sleep(Long.MAX_VALUE);
    }
}

实现效果

运行之前,需要在集群中创建servers节点。

[zk: localhost:2181(CONNECTED) 0] create /servers/

运行客户端,然后在集群中创建节点。

[zk: localhost:2181(CONNECTED) 1] create -e -s /servers/hadoop101 "hadoop101"
[zk: localhost:2181(CONNECTED) 2] create -e -s /servers/hadoop102 "hadoop102"
[zk: localhost:2181(CONNECTED) 3] create -e -s /servers/hadoop103 "hadoop103"
[zk: localhost:2181(CONNECTED) 4] ls /servers

集群中可以看到我们创建了三个节点,同时我们的客户端也在同步监听。

删除三个节点,客户端也能同步监听。

[zk: localhost:2181(CONNECTED) 5] delete /servers/hadoop1010000000000
[zk: localhost:2181(CONNECTED) 6] delete /servers/hadoop1020000000001
[zk: localhost:2181(CONNECTED) 7] delete /servers/hadoop1030000000002

运行服务端,报错停止后,我们点击Edit Configurations,将Program aruguments依次设置为hadoop101, hadoop102和hadoop103。分别运行,我们能看到服务端提示hadoop101上线,客户端也在同步监听。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值