ZookeeperClient(基于ZooKeeperNetEx再次封装)

概述

ZooKeeper本质上是一个分布式的小文件存储系统。原本是Apache Hadoop的一个组件,现在被拆分为一个Hadoop的独立子项目。

Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。

ZookeeperClient实现了断线重连,会话过期重连,永久监听,子节点数据变化的监听。并且加入了常用功能,例如分布式锁,Leader选举,分布式队列

使用说明

一、创建ZKClient对象
二、节点的新增、更新、删除和获取
三、权限管理
四、监听相关
五、扩展功能 

创建ZKClient对象 有两种方式可以方便的创建ZKClient对象

1.使用构造函数创建

string address = "localhost:2181"; 
    ZKClient zkClient1 = new ZKClient(address);     
    ZKClient zkClient2 = new ZKClient(address, TimeSpan.FromMilliseconds(10000));   
    ZKClient zkClient3 = new ZKClient(address, TimeSpan.FromMilliseconds(10000), TimeSpan.FromMilliseconds(10000));   
    ZKClient zkClient4 = new ZKClient(address, TimeSpan.FromMilliseconds(30000), TimeSpan.FromMilliseconds(10000), new SerializableSerializer());      
    ZKClient zkClient5 = new ZKClient(address, TimeSpan.FromMilliseconds(30000), TimeSpan.FromMilliseconds(10000), new SerializableSerializer(), TimeSpan.FromMilliseconds(60000));  

2.使用辅助类创建

  string address = "localhost:2181";  
    ZKClient zkClient = ZKClientBuilder.NewZKClient(address)  
                               .SessionTimeout(30000)//可选  
                               .Serializer(new SerializableSerializer())//可选  
                               .RetryTimeout(60000)//可选  
                               .ConnectionTimeout(10000)//可选  
                               .Build(); //创建实例

节点的新增、更新、删除和获取

新增节点

1.常规新增节点

父节点不存在会抛出异常

    await zkClient.CreateAsync("/test1", "123", CreateMode.EPHEMERAL);  
    await zkClient.CreateAsync("/test1-1", 123, CreateMode.EPHEMERAL_SEQUENTIAL);  
    await zkClient.CreateAsync("/test1-2", 123, CreateMode.PERSISTENT);  
    await zkClient.CreateAsync("/test1-3", 123, CreateMode.PERSISTENT_SEQUENTIAL);

2.递归新增节点(新增节点及其父节点)

如果父节点不存在会被一并创建。
对于PERSISTENT类型的节点,递归创建,父节点和子节点都创建为PERSISTENT。
对于EPHEMERAL类型的节点,递归创建,父节点都是PERSISTENT类型,而最后一级节点才是EPHEMERAL类型。(因为EPHEMERAL不能拥有子节点)
注意:第二个参数为节点的值,指的的最后一级节点的值。

     string path = "/test8/1/2/3";
     //递归创建节点及父节点
     await zkClient.CreateRecursiveAsync(path, "abc", CreateMode.PERSISTENT);
     await zkClient.CreateRecursiveAsync(path, "123", ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);

3.特殊的EPHEMERAL类型节点

特殊类型的EPHEMERAL节点,该节点在会话失效被删除后,重新连接会被自动创建。

       string path = "/test8/1/2/3";
       //EPHEMERAL类型节点
       await zkClient.CreateEphemeralAsync(path);
       await zkClient.CreateEphemeralAsync(path, "123");
       //EPHEMERAL_SEQUENTIAL类型
       string retPath = await zkClient.CreateEphemeralSequentialAsync(path, "456");

更新节点数据

   string path = "/test";
   await zkClient.SetDataAsync(path, "456");
   //带期望版本号的更新,如果真实的版本号与期望版本号不一致会更新失败,抛出异常
   await zkClient.SetDataAsync(path, "123", 2);

删除节点

1.常规删除

    bool flag = await zkClient.DeleteAsync("/test");//删除任意版本
    bool flag = await zkClient.DeleteAsync("/test",1);//删除指定版本

2.递归删除(删除节点及子节点)

    string path = "/test";
    await zkClient.DeleteRecursiveAsync(path);//如果/test下有多个子节点,会被一并删除

获取节点数据

    string path = "/test";
    await zkClient.GetDataAsync<string>(path); //如果节点不存在抛出异常
    await zkClient.GetDataAsync<string>(path, true); //如果节点不存在返回null
    Stat stat = (await zkClient.GetZKDataAsync<string>(path)).stat; //获得数据以及stat信息

等待节点创建

    string path = "/test";
    //等待直到超时或者节点创建成功。
    await zkClient.WaitUntilExistsAsync(path, TimeSpan.FromMilliseconds(5000));

权限管理

ZooKeeper的权限管理亦即ACL控制功能通过Server、Client两端协调完成:
Server端:
一个ZooKeeper的节点(znode)存储两部分内容:数据和状态,状态中包含ACL信息。创建一个znode会产生一个ACL列表,列表中每个ACL包括:

  • 验证模式(scheme)
  • 具体内容(Id)(当scheme=“digest”时,Id为用户名密码,例如“root:J0sTy9BCUKubtK1y8pkbL7qoxSw=”)
  • 权限(perms)

ZooKeeper提供了如下几种验证模式(scheme):

  • digest:Client端由用户名和密码验证,譬如user:password,digest的密码生成方式是Sha1摘要的base64形式
  • auth:不使用任何id,代表任何已确认用户。
  • ip:Client端由IP地址验证,譬如172.2.0.0/24
  • world:固定用户为anyone,为所有Client端开放权限
  • super:在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)

注意的是,exists操作和getAcl操作并不受ACL许可控制,因此任何客户端可以查询节点的状态和节点的ACL。

节点的权限(perms)主要有以下几种:

  1. Create 允许对子节点Create操作
  2. Read 允许对本节点GetChildren和GetData操作
  3. Write 允许对本节点SetData操作
  4. Delete 允许对子节点Delete操作
  5. Admin 允许对本节点setAcl操作
  6. Znode ACL权限用一个int型数字perms表示,perms的5个二进制位分别表示setacl、delete、create、write、read。比如0x1f=adcwr,0x1=—-r,0x15=a-c-r。
zkClient.AddAuthInfo(digest, Encoding.Default.GetBytes(allAuth));
            if (await zkClient.ExistsAsync(testNode))
            {
                await zkClient.DeleteAsync(testNode);
            }
            List<ACL> acls = new List<ACL>();
            acls.Add(new ACL((int)Perms.ALL, new Id(digest, allAuth)));
            acls.Add(new ACL((int)Perms.READ, new Id(digest, readAuth)));
            acls.Add(new ACL((int)Perms.WRITE, new Id(digest, writeAuth)));
            acls.Add(new ACL((int)Perms.DELETE, new Id(digest, deleteAuth)));
            acls.Add(new ACL((int)Perms.ADMIN, new Id(digest, adminAuth)));

            await zkClient.CreatePersistentAsync(testNode, "test-data", acls);

            try
            {
                await zkClient.GetDataAsync<string>(testNode);//没有认证信息,读取会出错
            }
            catch (Exception e) { }

            try
            {
                zkClient.AddAuthInfo(digest, Encoding.Default.GetBytes(adminAuth));
                await zkClient.GetDataAsync<string>(testNode);//admin权限与read权限不匹配,读取也会出错
            }
            catch (Exception e) { }

            try
            {
                zkClient.AddAuthInfo(digest, Encoding.Default.GetBytes(readAuth));
                await zkClient.GetDataAsync<string>(testNode);//只有read权限的认证信息,才能正常读取
            }
            catch (Exception e) { }

            try
            {
                await zkClient.SetDataAsync(testNode, "new-data");//没有认证信息,写入会失败
            }
            catch (Exception e) { }

            try
            {
                zkClient.AddAuthInfo(digest, Encoding.Default.GetBytes(writeAuth));
                await zkClient.SetDataAsync(testNode, "new-data");//加入认证信息后,写入正常
            }
            catch (Exception e) { }

            try
            {
                zkClient.AddAuthInfo(digest, Encoding.Default.GetBytes(readAuth));
                await zkClient.GetDataAsync<string>(testNode);//读取新值验证
            }
            catch (Exception e) { }

            try
            {
                zkClient.AddAuthInfo(digest, Encoding.Default.GetBytes(deleteAuth));
                await zkClient.DeleteAsync(testNode);
            }
            catch (Exception e) { }

            //注:zkClient.setAcl方法查看源码可以发现,调用了readData、setAcl二个方法
            //所以要修改节点的ACL属性,必须同时具备read、admin二种权限        
            try
            {
                zkClient.AddAuthInfo(digest, Encoding.Default.GetBytes(adminAuth));
                zkClient.AddAuthInfo(digest, Encoding.Default.GetBytes(readAuth));

                List<ACL> acls1 = new List<ACL>();
                acls1.Add(new ACL((int)Perms.ALL, new Id(digest, adminAuth)));
                await zkClient.SetACLAsync(testNode, acls1);
                ACLResult aclResult = await zkClient.GetACLAsync(testNode);
            }
            catch (Exception e) { }

监听相关

注意:对于断开连接时间过长造成的会话过期,由于服务器端在会话过期后会删除客户端设置的监听。

即便客户端在会话过期后自动连接成功,但是在会话过期到会话重建这段时间客户端监听的节点仍可能发生了改变,

而具体哪些变了或是没变,客户端是无法感知到的。

为了避免丢掉任何数据改变的事件,所有的监听器的都有一个回调方法(SessionExpiredHandler),用来处理会话过期这种特殊情况。

    SessionExpiredHandler = async (path) =>
                {
                    await Task.Run(() =>
                    {
                        Console.WriteLine(path);
                    });
                };

订阅节点的信息改变(创建节点,删除节点,添加子节点)

 IZKChildListener childListener = new ZKChildListener();
    //子节点内容变化
    childListener.ChildChangeHandler = async (parentPath, currentChilds) =>
         {
               await Task.Run(() =>
               {
                     Console.WriteLine(parentPath);
                     Console.WriteLine(string.Join(".", currentChilds));
               });
         };
     //子节点数量变化
     childListener.ChildCountChangedHandler = async (parentPath, currentChilds) =>
         {
               await Task.Run(() =>
               {
                     Console.WriteLine(parentPath);
                     Console.WriteLine(string.Join(".", currentChilds));
               });
         };
     //"/testUserNode" 监听的节点,可以是现在存在的也可以是不存在的 
     zkClient.SubscribeChildChanges("/testUserNode3", childListener);   

订阅节点的数据内容的变化

    IZKDataListener dataListener = new ZKDataListener();
    // 节点创建和节点内容变化
    dataListener.DataCreatedOrChangeHandler = async (dataPath, data) =>
        {
                await Task.Run(() =>
                {
                      Console.WriteLine(dataPath + ":" + Convert.ToString(data));
                });
        };
     // 节点删除
     dataListener.DataDeletedHandler = async (dataPath) =>
         {
                 await Task.Run(() =>
                 {
                      Console.WriteLine(dataPath);
                 });

         };
      // 节点创建
      dataListener.DataCreatedHandler = async (dataPath, data) =>
          {
                   await Task.Run(() =>
                   {
                       Console.WriteLine(dataPath + ":" + Convert.ToString(data));
                   });
           };
       // 节点内容变化
       dataListener.DataChangeHandler = async (dataPath, data) =>
            {
                    await Task.Run(() =>
                    {
                        Console.WriteLine(dataPath);
                     });

            };
      zkClient.SubscribeDataChanges("/testUserNode", dataListener);

客户端状态监听

    IZKStateListener stateListener = new ZKStateListener();
    //状态改变
    stateListener.StateChangedHandler = async (state) =>
         {
                await Task.Run(() =>
                {
                   Console.WriteLine(state.ToString());
                });
         };
      //会话失效
      stateListener.SessionExpiredHandler = async (path) =>
           {
                  await Task.Run(() =>
                  {
                     Console.WriteLine(path);
                  });
           };
       //创建会话
        stateListener.NewSessionHandler = async () =>
             {
                   await Task.Run(() =>{});
             };
        //会话失败
        stateListener.SessionEstablishmentErrorHandler = async (ex) =>
             {
                  await Task.Run(() =>
                   {
                        Console.WriteLine(ex.Message);
                  });

             };
         zkClient.SubscribeStateChanges(stateListener);

扩展功能

分布式锁

 using (var zkClient = new ZKClient(TestUtil.zkServers))
 {    
         await zkClient.CreateRecursiveAsync("/zk/lock", null, CreateMode.Persistent);

         //创建分布式锁, 非线程安全类,每个线程请创建单独实例。
         var _lock = new ZKDistributedLock(zkClient, "/zk/lock");

         await _lock.LockAsync(); //获得锁

         //do someting

         await _lock.UnLockAsync();//释放锁
}

Leader选举

 using (var zkClient = new ZKClient(TestUtil.zkServers))
 {          
         await zkClient.CreateRecursiveAsync("/zk/leader", null, CreateMode.Persistent);

         var listener = new ZKLeaderSelectorListener();
         listener.takeLeadership = async (client, selector) =>
                                 {                 
                                     Console.WriteLine("I am the leader-" + await selector.GetLeaderAsync());
                                     selector.Close();
                                 };
         var selector = new ZKLeaderSelector("id", true, zkClient, "/zk/leader", listener);
        //启动并参与Leader选举
         selector.Start();

         //获得当前主服务的ID
         await selector.GetLeaderAsync();

         //如果要退出Leader选举
         selector.Close();
}

分布式队列

using (var zkClient = new ZKClient(TestUtil.zkServers))
 {
         await zkClient.CreateRecursiveAsync("/zk/queue", null, CreateMode.PERSISTENT);

         var queue = new ZKDistributedQueue<long>(new ZKClient(TestUtil.zkServers), "/zk/queue")

         await queue.OfferAsync("123");//放入元素

         var value = await queue.PollAsync();//删除并获取顶部元素

         var value =  await queue.PeekAsync(); //获取顶部元素,不会删除    
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
什么是ZooKeeperZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。 ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 Rabbit ZooKeeper Extensions 该项目使用了 Apache ZooKeeper .NET async Client 组件,除提供了基本的zk操作,还额外封装了常用的功能以便让.net开发者更好的使用zookeeper。 提供的功能 session过期重连 永久watcher 递归删除节点 递归创建节点 跨平台(支持.net core) 使用说明 创建连接 IZookeeperClient client = neZookeeperClient(neZookeeperClientOptions         {             ConnectionString = "172.18.20.132:2181",             BasePath = "/", //default value             ConnectionTimeout = TimeSpan.FromSeconds(10), //default value             SessionTimeout = TimeSpan.FromSeconds(20), //default value             OperatingTimeout = TimeSpan.FromSeconds(60), //default value             ReadOnly = false, //default value             SessionId = 0, //default value             SessionPasswd = null //default value         }); 创建节点 var data = Encoding.UTF8.GetBytes("2016"); //快速创建临时节点 await client.CreateEphemeralAsync("/year", data); await client.CreateEphemeralAsync("/year", data, ZooDefs.Ids.OPEN_ACL_UNSAFE); //快速创建永久节点 await client.CreatePersistentAsync("/year", data); await client.CreatePersistentAsync("/year", data, ZooDefs.Ids.OPEN_ACL_UNSAFE); //完整调用 await client.CreateAsync("/year", data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); //递归创建 await client.CreateRecursiveAsync("/microsoft/netcore/aspnet", data, CreateMode.PERSISTENT); 获取节点数据 IEnumerable data = await client.GetDataAsync("/year"); Encoding.UTF8.GetString(data.ToArray()); 获取子节点 IEnumerable children= await client.GetChildrenAsync("/microsoft"); 判断节点是否存在 bool exists = await client.ExistsAsync("/year"); 删除节点 await client.DeleteAsync("/year"); //递归删除 bool success = await client.DeleteRecursiveAsync("/microsoft"); 更新数据 Stat stat = await client.SetDataAsync("/year", Encoding.UTF8.GetBytes("2017")); 订阅数据变化 await client.Subscrib

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值