Zookeeper(CP)

以集群的方式【leader和follower】为分布式应用提供协调服务、负责存储和管理大家都关系的数据,接受观察者注册、消息分发等服务

特点:
  1. 只要有半数以上的节点存活就能保证zookeeper的集群正常工作

  2. 每一个Client保存一份相同的数据副本

  3. 来自同一个client的跟新请求可以顺序执行

  4. 数据更新原子性,且在一定时间范围内,client能读到最新的数据

  5. 软负载均衡:可以让访问数最少的服务器去处理最新服务器请求

启动命令

#启动zk服务器,查询zk服务器状态
./zkServer.sh status
./zkServer.sh start path/to/zoo.config
./zkServer.sh stop
#连接服务器
./zkCli.sh

客户端命令行

create treeNode data #在某个节点下存放数据
create -s treeNode data #有序号的节点,记录节点创建的先后顺序,没有从零开始
ls TreeNode # 获取某个树节点下的所有数据
delete TreeNode #删除节点,如果下面还有子节点是无法删除的
rmr Tree #删除某个子树

get treeNode #获取某个节点下的数据
get treeNode watch #注册对某个节点修改的监听,只会有效一次
set treeNode data #改变值
get path #获取某个树形结构下的值
get -w path #获取某个树形结构下的值,并监听一次变化
helo #帮助手册
quit#退出
短暂数据:
create -e treeNode data #在某个节点下存放数据,当前客户端与服务器断开则节点消失
节点参数:

czxid:创建该节点的事务ID、mzxid:最后更新的事务ID、cversion:节点的修改次数、dataversion:数据变化号

节点类型【树形结构】

持久节点,顺序【名字递增】&非顺序:persistent、persistent_sequential,除了在内存中,在磁盘中也会保存一份

临时节点,顺序&非顺序:ephemeral、ephemeral_sequential 短暂节点在与客户端断开连接后,创建的节点自己删除了。其原理是通过ping去保活,如果断开,则会发生session-timeOut,顺序的临时节点适合用来充当分布式锁。相比redis集群而言,zookeeper集群具备更好的数据一致性。另外临时节点不允许创建子节点

容器节点:当没有子节点时,会被服务器删除【默认60s检查一次】

TTL节点:过了TTL指定节点的时间就会被服务器删除,和redis一致

集群搭建

在.cfg文件中设置存储数据的目录,同时在目录下touch一个myid,并设置一个唯一标识

配置文件:默认是/conf/zoo.cfg

server.A1=B:C:D
server.A2=B:C:D
server.A3=B:C:D
#Ai:myid,唯一的数字标识,在数据存储目录中myid文件里定义
#B:服务器的IP地址
#C:leader服务器交换信息的端口号
#D:传递选举信息的端口号

当半数启动后集群开始正常运转【一般三个或以上】

节点的读写

在zookeeper中,某个节点接受到客户端写请求,会将请求转发给leader,由leader广播写请求,并统计成功数。当超过半数回复成功时,才会向客户端返回成功

CAP:

CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容忍性(Partition tolerance):将数据项复制到多个节点上,当某些节点发生故障,仍然可以访问到数据的能力

整合spring

连接并设置监听是否变化,产生变化后再次监听:不依赖spring,只依赖一个zookeeper.jar包,如果是Spring框架,可以通过配置pean的方式,直接注入 zookeeper
public class ZooKeeperTest {
    private int sessionTimeout = 2000;
    private String connectString="222.201.144.247:2181";
    public ZooKeeper zooKeeper;
    @Test
    public void init() throws IOException, KeeperException, InterruptedException {

        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                try {
                    System.out.println("_________________start___________________");
                    List<String> list = zooKeeper.getChildren("/",true);
                    for (int i = 0; i < list.size(); i++) {
                        System.out.println(list.get(i));
                    }
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
//        String s=zooKeeper.create("/connect", "spring".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        zooKeeper.getChildren("/",true);//设置监听,只会生效一次
        Thread.sleep(Long.MAX_VALUE);
    }
}
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.10</version>
    </dependency>
监听服务器上下线:

服务器集群向Zookeeper集群上传访问量总量,且数据类型为ephemeral,服务器掉线则数据消失

客户端集群向Zookeeper监听数据getChildren(),服务器宕机、上线时做出处理并重设监听

//客户机,主机部分向Zookeeper集群里的/下写上带序号、ephemeral的key 和 hostName即可,而由客户机负责监听
public class DistributeClient {

    public static void main(String[] args) throws KeeperException, InterruptedException, IOException {
        DistributeClient client = new DistributeClient();
        //连接到Zookeeper集群
        client.getConnect();
        //设置监听在控制台打印节点
        client.getChildren();
        //业务部分
        client.business();
    }

    private int sessionTimeout = 2000;
    private String connectString="222.201.144.247:2181";//如果是Zookeeper集群的话直接在用逗号隔开即可
    public ZooKeeper zooKeeper;

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

    private void getChildren() throws KeeperException, InterruptedException {
        List<String> children = zooKeeper.getChildren("/",true);

        ArrayList<String> host = new ArrayList<>();
        for (String child :children){
            byte[] data = zooKeeper.getData("/"+child,false,null);
            host.add(new String(data));
        }
        System.out.println(host);
    }

    private void business(){
    }

}

利用zookeeper实现分布式锁

方法一:

获取锁:create -e /ephemeral/lock threadName 创建失败则监听 get -w /ephemeral/lock【此时处于阻塞状态】,利用Watcher重新获取锁。弊端:所有的线程都需要反复的监听,尝试获取锁,产生系统资源浪费

方法二:【只能实现公平锁】

请求直接在 create -e -s /ephemeral/lock threadName 创建一个临时顺序节点,并判断自己是否是最小的节点,如果是,则获取锁,若不是,则只监听前一个节点【只比其小一的节点】并等待。最后通过delete释放锁

 posted on 2022-03-17 10:40  春秋流千事  阅读(72)  评论(0)    收藏  举报