Zookeeper学习笔记

Zookeeper是一个高性能,分布式的,开源分布式应用协调服务。
1、源代码开发
2、分布式协调服务
2.1 顺序一致性
2.2 原子性
2.3 单一视图
2.4 可靠性
2.5 实时性
3、高性能
4、简单的api实现复制的功能


常见应用场景
1、配置中心
2、负载均衡
3、统一命名服务
4、共享锁


数据模型

集群角色
1、Leader:接受所有的Follower的提案请求并统一协调发起提案的投票,负责与所有Follower进行内部的数据交换(同步);
2、Follower:直接为客服端服务并参与提案的投票,同时与Leader进行数据交换(同步);
3、Observer:直接为客服端服务但不参与提案的投票,同时与Leader进行数据交换(同步);
集群角色


会话
1、Client初始化连接,状态转为CONNECTING(①)
2、Client与Server成功建立连接,状态转为CONNECTED(②)
3、Client丢失了与Server的连接货没有接收到Server的响应,状态转为CONNECTING(③)
4、Client练手另外的Server或连接上了之前的Server,状态转为CONNECTED(②)
5、若会话过期(是Server复制声明会话过期,状态转为CLOSED(⑤))
6、Cliend也可以主动关闭会话(④),状态转为CLOSED
会话


版本号
cversion
dataversion
aclversion


Watcher


Acl权限控制


Linux下单机Zookeeper搭建
1、下载解压zookeeper
2、cd conf ; cp zoo_sample.cfg zoo.cfg; vim zoo.cfg
3、修改dataDir=/home/zookeeper/zookeeper1/data
clientPort=5000
4、启动Zookeeper:
./zkServer.sh start
5、连接Zookeeper,-r只读
./zkCli.sh [-timeout 0 -r] -server localhost:5000


Linux下Zookeeper集群搭建
1、创建3台服务,zookeeper1,zookeeper2,zookeeper3
2、复制修改配置文件zoo.cfg,端口号分别为5000,5001,5002
#格式server = ip:port(通信端口):port(选举端口)

所有配置
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/zookeeper/zookeeper1/data
clientPort=5000
tickTime=2000
server.1=10.135.54.100:5100:5101
server.2=10.135.54.100:5102:5103
server.3=10.135.54.100:5104:5105

Zookeeper节点类型
1、持久化节点 create /node_1 data
2、持久化有序节点 create -s /node_1 data
3、临时节点 create -e /node_1 data session过期,节点就会删除
4、临时有序节点 create -s -e /node_1 data


Zookeeper基本操作
1. create [-s] [-e] path data acl 创建节点
2. ls path [watch] 查看节点目录
3. get path [watch]
“`
cZxid = 0x1000000dd
ctime = Thu Jan 19 10:08:16 CST 2017
mZxid = 0x1000000dd
mtime = Thu Jan 19 10:08:16 CST 2017
pZxid = 0x1000000dd
cversion = 0 子节点版本号
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0 sessionid
dataLength = 5
numChildren = 0

  1. set path data [version] 修改节点数据
  2. delete path [version] 删除节点
  3. setquota -n|-b val path -n子节点数量,-b数据容量。虽然设置了限制,但是仍然是可以创建成功。同时,会bin/zookeeper.out 输出警告信息
    7.delquota [-n|-b] path 删除限制
    1. listquota path 查看限制信息

Zookeeper JavaApi

//连接ZooKeeper 
ZooKeeper zk = new ZooKeeper("139.199.182.26:5001",SESSION_TIMEOUT , new Watcher() {
                public void process(WatchedEvent event) {
                    System.out.println("事件"+event.getType());
                }
            });
/**权限控制
 三种权限范围:
    OPEN_ACL_UNSAFE : 对所有用户开放
    READ_ACL_UNSAFE : 只读
    CREATOR_ALL_ACL: 创建者可以做任何操作
**/
//创建节点
zk.create("/node_1", "hello".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
//更新节点,-1表示所有vesion
zk.setData("/node_1", "hello world".getBytes(), -1);
//删除节点,-1表示所有vesion
zk.delete("/node_1", -1);
//权限
ArrayList<ACL> list = new ArrayList<ACL>();
ACL acl = new ACL(ZooDefs.Perms.ALL, new Id("digest", DigestAuthenticationProvider.generateDigest("root:123456")));
list.add(acl);
zk.create("/node_1", "hello".getBytes(),list, CreateMode.PERSISTENT);

ZkClient
zkClient主要做了两件事情。一件是在session loss和session expire时自动创建新的ZooKeeper实例进行重连。另一件是将一次性watcher包装为持久watcher。后者的具体做法是简单的在watcher回调中,重新读取数据的同时再注册相同的watcherpublic class ZkClientDemo {

private static String CONNECT_STRING="120.77.22.187:2181,120.77.22.187:2182,120.77.22.187:2183";

private static int SESSION_TIMEOUT=3000;

public static void main(String[] args) {
    ZkClient zkClient=new ZkClient(CONNECT_STRING,SESSION_TIMEOUT,SESSION_TIMEOUT,new MyZkSerializer());
    try {
        zkClient.subscribeChildChanges("/configuration", new IZkChildListener() {
            @Override
            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                System.out.println("触发事件:"+parentPath);
                for(String str:currentChilds){
                    System.out.println(str);
                }
            }
        });
        System.in.read();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        zkClient.close();
    }
}
private static void create(ZkClient zk){
    zk.createPersistent("/node_11/node_11_1/node_11_1_1",true); //递归创建节点
}

private static void update(ZkClient zk){
    zk.writeData("/node_11","zyz");
}

private static void delete(ZkClient zk){
    boolean bool=zk.deleteRecursive("/node_11");
    System.out.println(bool);
}

private static void subWatch(ZkClient zk){
    if(!zk.exists("/node_11")) {
        zk.createPersistent("/node_11");
    }
    //数据订阅事件
    zk.subscribeDataChanges("/node_11", new IZkDataListener() {
        @Override
        public void handleDataChange(String dataPath, Object data) throws Exception {
            System.out.println("触发事件:"+dataPath+"->"+data);
        }

        @Override
        public void handleDataDeleted(String dataPath) throws Exception {
            System.out.println("触发删除事件:"+dataPath);
        }
    });
}

}
public class MyZkSerializer implements ZkSerializer{
@Override
public byte[] serialize(Object data) throws ZkMarshallingError {
try {
return String.valueOf(data).getBytes(“UTF-8”);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}

@Override
public Object deserialize(byte[] bytes) throws ZkMarshallingError {
    try {
        return new String(bytes, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return null;
}

}


Curator
Curator是Netflix开源的一套ZooKeeper客户端框架
封装ZooKeeper client与ZooKeeper server之间的连接处理;
提供了一套Fluent风格的操作API;
提供ZooKeeper各种应用场景(recipe, 比如共享锁服务, 集群领导选举机制)的抽象封装.

Curator几个组成部分
Client: 是ZooKeeper客户端的一个替代品, 提供了一些底层处理和相关的工具方法.
Framework: 用来简化ZooKeeper高级功能的使用, 并增加了一些新的功能, 比如管理到ZooKeeper集群的连接, 重试处理
Recipes: 实现了通用ZooKeeper的recipe, 该组件建立在Framework的基础之上
Utilities:各种ZooKeeper的工具类 Errors: 异常处理, 连接, 恢复等. Extensions: recipe扩展

Curator 重试策略
1. ExponentialBackoffRetry 衰减重试
2. RetryNTimes 指定最大重试次数
3. RetryOneTime 只重试一次
4. RetryUntilElased 一直重试直到规定时间

监听器
New watcher
CuratorListener
pathChildCacheListenner
NodeCacheListenner
TreeCacheListenner

public class CuratorDemo {

    private static String CONNECT_STRING="120.77.22.187:2181,120.77.22.187:2182,120.77.22.187:2183";

    private static int SESSION_TIMEOUT=3000;

    public static void main(String[] args) throws Exception {
        //TODO 连接zookeeper
        CuratorFramework framework=CuratorFrameworkFactory.
                newClient(CONNECT_STRING,SESSION_TIMEOUT,SESSION_TIMEOUT,new ExponentialBackoffRetry(1000,10));

        framework.start();
//        create(framework);
//        update(framework);
//        delete(framework);
//        transaction(framework);
        listener2(framework);
        System.in.read();
        System.out.println(framework.getState()); //获取连接状态
    }

    private static void create(CuratorFramework cf){
        try {
            String rs=cf.create().withMode(CreateMode.EPHEMERAL).inBackground().forPath("/node_14/node_14_1","zzy".getBytes());
            System.out.println(rs);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            cf.close();
        }
    }

    private static void update(CuratorFramework cf) throws Exception {
        Stat stat=cf.setData().forPath("/node_13/node_13_1","xyz".getBytes());
        System.out.println(stat);
        cf.close();
    }

    private static void delete(CuratorFramework cf){
        try {
            cf.delete().deletingChildrenIfNeeded().forPath("/node_2"); //递归删除的话,则输入父节点
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            cf.close();
        }
    }

    private static void transaction(CuratorFramework cf){
        try {
            //事务处理, 事务会自动回滚
            Collection<CuratorTransactionResult> results=cf.inTransaction().create().
                    forPath("/node_2").and().create().forPath("/node_3").and().commit();
            for(CuratorTransactionResult result:results){
                System.out.println(result.getResultStat()+"->"+result.getForPath()+"->"+result.getType());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void listener(CuratorFramework cf){
        try {
            cf.getData().usingWatcher(new CuratorWatcher() {
                @Override
                public void process(WatchedEvent event) throws Exception {
                    System.out.println("触发事件"+event.getType());
                }
            }).forPath("/node_3"); //通过CuratorWatcher 去监听指定节点的事件, 只监听一次

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
           // cf.close();
        }
    }

    private static void listener2(CuratorFramework cf) throws Exception {
        //子节点监听
        PathChildrenCache childrenCache=new PathChildrenCache(cf,"/node_3",true);
        //NodeCache nodeCache;
        childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                System.out.println(event.getType()+"事件监听2");
            }
        });
       // cf.getCuratorListenable().addListener();


    }
}

ZAB协议
ZAB协议是为分布式协调服务zookeeper专门设计的一种支持崩溃恢复的原子广播协议
在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。

ZAB协议包括两种基本模式
1. 崩溃恢复
leader出现网络中端、或者服务不可用。Zab协议就会进入恢复模式
投票数量过半
当leader选举出来以后,zab协议就会进入消息广播状态
2. 消息广播
消息同步
查看事务日志的命令

java -cp ../../zookeeper-3.4.9/zookeeper-3.4.9.jar:../../zookeeper-3.4.9/lib/slf4j-api-1.6.1.jar org.apache.zookeeper.server.LogFormatter log.200000f73

Zookeeper注意事项
集群里分三种角色: Leader, Follower和Observer。Leader和Follower参与投票,Observer只会『听』投票的结果,不参与投票
一个集群容忍挂掉的节点数的等式为 N = 2F + 1,N为投票集群节点数,F为能同时容忍失败节点数
一个写操作需要半数以上的节点ack,所以集群节点数越多,整个集群可以抗挂点的节点数越多(越可靠),但是吞吐量越差

集群服务器数
网络
内存
事务日志清理
日志,jvm配置
日志位置
地址

不要强依赖Zookeeper

不要使用Zookeeper做细粒度锁

不要将很多东西塞到Zookeeper里

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章