zookeeper 初探

zookeeper 安装及使用

1. 简介

ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications.

简单来说,zookeeper就是做来做同步服务,配置维护和命名服务的。

2. 安装(需首先安装jdk)

- 2.1 下载

点击下载 zookeeper-3.4.10.tar.gz 的jar包。

- 2.2 机器环境准备

因为zookeeper是过半可用,所以一般是奇数台机器,偶数的话有点浪费资源,故准备3个版本为contos7的虚拟机进行安装(192.168.132.129,192.168.132.130,192.168.132.131)。

- 2.3 安装
  • 首先将zookeeper-3.4.10.tar.gz拷贝到其中的一台机器的某个安装目录,在笔者的机器里是 安装地址
  • 使用命令进行解压: tar -zxvf zookeeper-3.4.10.tar.gz
  • 修改配置文件,conf 下的zoo_sample.cfg 改为zoo.cfg并参考以下配置为:

    # ZK中的一个时间单元。ZK中所有时间都是以这个时间单元为基础,进行整数倍配置的。例如,session的最小超时时间是2*tickTime。默认3000毫秒。这个单元时间不能设置过大或过小,过大会加大超时时间,也就加大了集群检测session
    失效时间;设置过小会导致session很容易超时,并且会导致网络通讯负载较重(心跳时间缩短)
    tickTime=2000
    # Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。Leader允许Follower在initLimit时间内完成这个工作。通常情况下,我们不用太在意这个参数的设置。如果ZK集群的数据量确实很大了,F
    ollower在启动的时候,从Leader上同步数据的时间也会相应变长,因此在这种情况下,有必要适当调大这个参数了。默认值为10,即10 * tickTime  (No Java system property)
    initLimit=10
    # 在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果Leader发出心跳包在syncLimit之后,还没有从Follower那里收到响应,那么就认为这个Follower已经不在线了。注意
    :不要把这个参数设置得过大,否则可能会掩盖一些问题,设置大小依赖与网络延迟和吞吐情况。默认为5,即5 * tickTime (No Java system   property) 
    syncLimit=5
    # 就是把内存中的数据存储成快照文件snapshot的目录,同时myid也存储在这个目录下(myid中的内容为本机server服务的标识)。写快照不需要单独的磁盘,而且是使用后台线程进行异步写数据到磁盘,因此不会对内存数据有影响。默认
    情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir, 事务日#志的写性能直接影响zk性能。在建立myid文件时,一定要放在dataDir那个目录下
    dataDir=/da/data/zookeeper/zkData
    #事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升ZK性能。 由于事务日志输出时是顺序且同步写到磁盘,只有从磁盘写完日志后才会触发follower和leader发回事务日志确认消息(zk事务采用两阶段
    提交),因此需要单独磁盘避免随机读写和磁盘缓存导致事务日志写入较慢或存储在缓存中没有写入。
    dataLogDir=/da/data/zookeeper/logs
    #客户端连接server的端口,即zk对外服务端口,一般设置为2181。
    clientPort=2181
    # the maximum number of client connections.
    # increase this if you need to handle more clients
    #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
    server.1=data1.da.org:2888:3888
    server.2=data2.da.org:2888:3888
    server.3=data3.da.org:2888:3888
  • 创建:zoo.cfg 文件中的dataDir 和 dataLogDir ,并且在dataDir 中创建一个myid 文件,值为配置文件最后的 配置的值,例:如果当前主机是data1.da.org 则 myid内为1 (注:这里的域名是笔者自己在/etc/hosts 文件中配置的)
  • 使用scp -r 命令将此文件夹拷贝到其他的两个机器中,并创建对应的目录(注: myid文件需要修改为对应的值)
  • 启动命令为 /bin/zkServer.sh start
  • 查看是是否启动成功命令为 /bin/zkServer.sh status
  • 如果启动失败的话可以查看日志文件进行调试 ,日志在bin目录下

3. curator 使用

- 3.1. 简介

Apache Curator is a Java/JVM client library for Apache ZooKeeper, a distributed coordination service. It includes a highlevel API framework and utilities to make using Apache ZooKeeper much easier and more reliable. It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL.

因为zookeeper的原生api不是很好用,所以一般都使用curator 来对zookeeper进行控制

- 3.2. 使用

Apache Curator官网 有非常详细的例子,笔者建议大家查阅官网的examples,此处只是笔者只简单介绍.

maven 依赖


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- zookeeper -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.10</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-client</artifactId>
            <version>4.0.1</version>
        </dependency>

单元测试

“`

package com.zookeeper;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AppTest 
{
    /** zookeeper地址 */
    private static final String CONNECT_ADDR = "192.168.132.129:2181,192.168.132.130:2181,192.168.132.131:2181";
    /** session超时时间 */
    private static final int SESSION_OUTTIME = 5000; //ms
    private static CuratorFramework cf;

    @Before
    public void createSession(){
        //1 重试策略:初试时间为1s 重试10次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
        cf = CuratorFrameworkFactory.builder()
                    .connectString(CONNECT_ADDR)
                    .sessionTimeoutMs(SESSION_OUTTIME)
                    .retryPolicy(retryPolicy)
                    .namespace("test")
                    .build();
        cf.start();
    }

    @Test
    public void checkStateTest()
    {
        System.out.println(cf.getState());
    }

    /**
     * 测试增加以及删除数据
     * @throws Exception
     */
    @Test
    public void createAndDeleteTest() throws Exception {
        final Stat stat = cf.checkExists().forPath("/xh/test01");
        if (null == stat){
            cf.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/xh/test01","test01".getBytes());
        }else{
            cf.delete().guaranteed().deletingChildrenIfNeeded().forPath("/xh/test01");
        }
    }

    /**
     * 事务测试
     * @throws Exception
     */
    @Test
    public void TransactionTest() throws Exception {
        cf.inTransaction().check().forPath("/xh/test01")
                .and()
                .create().withMode(CreateMode.EPHEMERAL).forPath("/xh/test01/test01","test02".getBytes())
                .and()
                .setData().forPath("/xh/test01/test01","test02".getBytes())
                .and()
                .commit();
    }

    /**
     * 监听测试
     *  Path Cache:监视一个路径下1)孩子结点的创建、2)删除,3)以及结点数据的更新。产生的事件会传递给注册的PathChildrenCacheListener。
     *  Node Cache:监视一个结点的创建、更新、删除,并将结点的数据缓存在本地。
     *  Tree Cache:Path Cache和Node Cache的“合体”,监视路径下的创建、更新、删除事件,并缓存路径下所有孩子结点的数据。
     */

    //1.path Cache  连接  路径  是否获取数据
    //能监听所有的字节点 且是无限监听的模式 但是 指定目录下节点的子节点不再监听
    @Test
    public void listenterPathTest() throws Exception{

        ExecutorService pool = Executors.newCachedThreadPool();
        PathChildrenCache childrenCache = new PathChildrenCache(cf, "/xh", true);
        PathChildrenCacheListener childrenCacheListener = new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                ChildData data = event.getData();
                switch (event.getType()) {
                    case CHILD_ADDED:
                        System.out.println("增加节点 : "+ data.getPath() +"  数据:"+ data.getData().toString());
                        break;
                    case CHILD_REMOVED:
                        System.out.println("删除节点 : "+ data.getPath() +"  数据:"+ data.getData().toString());
                        break;
                    case CHILD_UPDATED:
                        System.out.println("更新节点 : "+ data.getPath() +"  数据:"+ data.getData().toString());
                        break;
                    default:
                        break;
                }
            }
        };
        // 增加监听
        childrenCache.getListenable().addListener(childrenCacheListener);
        childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        // 阻塞当前线程
        Thread.sleep(Long.MAX_VALUE);
    }

    //2.Node Cache  监控本节点的变化情况   连接 目录 是否压缩
    //监听本节点的变化  节点可以进行修改操作  删除节点后会再次创建(空节点)
    @Test
    public void listenterNodeCacheTest() throws Exception{
        ExecutorService pool = Executors.newCachedThreadPool();
        //设置节点的cache
        final NodeCache nodeCache = new NodeCache(cf, "/xh", false);
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                System.out.println("path : "+nodeCache.getCurrentData().getPath());
                System.out.println("data : "+new String(nodeCache.getCurrentData().getData()));
                System.out.println("stat : "+nodeCache.getCurrentData().getStat());
            }
        });
        nodeCache.start();
        // 阻塞当前线程
        Thread.sleep(Long.MAX_VALUE);
    }

    //3.Tree Cache
    // 监控 指定节点和节点下的所有的节点的变化--无限监听  可以进行本节点的删除(不在创建)
    @Test
    public void listenterTreeCacheTest() throws Exception{
        ExecutorService pool = Executors.newCachedThreadPool();
        //设置节点的cache
        TreeCache treeCache = new TreeCache(cf, "/xh");
        //设置监听器和处理过程
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                ChildData data = event.getData();
                if(data !=null){
                    switch (event.getType()) {
                        case NODE_ADDED:
                            System.out.println("增加节点 : "+ data.getPath() +"  数据:"+ new String(data.getData()));
                            break;
                        case NODE_REMOVED:
                            System.out.println("删除节点 : "+ data.getPath() +"  数据:"+ new String(data.getData()));
                            break;
                        case NODE_UPDATED:
                            System.out.println("更新节点 : "+ data.getPath() +"  数据:"+ new String(data.getData()));
                            break;

                        default:
                            break;
                    }
                }else{
                    System.out.println( "data is null : "+ event.getType());
                }
            }
        });
        //开始监听
        treeCache.start();
        // 阻塞当前线程
        Thread.sleep(Long.MAX_VALUE);
    }

    @After
    public void closeSession(){
        CloseableUtils. closeQuietly(cf);
    }

}

“`

4. 参考资料

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值