• 读《分布式一致性原理》JAVA客户端API操作3


     

    更新数据

    客户端可以通过zookeeper的API来更新一个节点的数据内容,有如下两个接口:

    public Stat setData(final String path, byte data[], int version)
     public void setData(final String path, byte data[], int version,
                StatCallback cb, Object ctx)

    更新数据的接口较为简单明了。我们重点来看下方法中的version参数。version参数是指定节点的数据版本。表明本次更新是针对指定版本进行的。

    《java并发编程实践》一书提到,在现代的绝大数计算机处理器体系架构中,都实现了对CAS指令支持,

    通俗的将,CAS的意思是“对于值V,每次更新都会对其值是否是值是否是预期值A,只有符合预期,才会将A原子化的更新为B”。zookeeper中setData接口中的version参数正是由CAS原理演化而来的。

    具体来说,假如有个客户端想要进行更新操作,那么肯定会携带上次获取的version值进行更新。如果在这段时间,该数据恰好被其他客户端更新了,那么其数据版本一定也发生了变化,因此肯定与客户端携带的version无法匹配,于是便无法更新成功。可以有效的避免分布式更新的并发问题。

    使用同步API更新节点数据内容

    package setdata;
    
    import java.io.IOException;
    import java.util.concurrent.CountDownLatch;
    
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.WatchedEvent;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.ZooKeeper;
    import org.apache.zookeeper.Watcher.Event.EventType;
    import org.apache.zookeeper.Watcher.Event.KeeperState;
    import org.apache.zookeeper.ZooDefs.Ids;
    import org.apache.zookeeper.data.Stat;
    
    import getdata.GetData1;
    
    public class SetData1 implements Watcher {
    
        public static CountDownLatch connectedSemaphore = new CountDownLatch(1);
        private static ZooKeeper zk = null;
        private static Stat stat = new Stat();
        
            @Override
            public void process(WatchedEvent event) {
                if (KeeperState.SyncConnected==event.getState()) {
                    if (EventType.None.getIntValue()==event.getState().getIntValue()&&null==event.getPath()) {
                        connectedSemaphore.countDown();
                    }
                    
                }
                
            }
            public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
                String path = "/zk-book";
                 zk = new ZooKeeper("192.168.64.60:2181", 5000, new SetData1());
                connectedSemaphore.await();
                
                zk.create(path, "123".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                Stat stat2 = zk.setData(path, "456".getBytes(), -1);
                
                System.out.println(stat.getCzxid()+"---"+stat.getMzxid()+"--"+stat.getVersion());
                Thread.sleep(Integer.MAX_VALUE);;
    
            }
    
    }

    在zookeeper中,数据版本都是从0开始的,所以严格来讲-1并不是一个数据版本,它仅仅是一个

    标示符,是告诉服务器对最新版本进行更新操作。如果对zookeeper数据节点的更新操作没有原子性要求,那么就可以使用“-1”;

    所以用异步的API更新数据内容

    package getdata;
    
    import java.io.IOException;
    import java.util.concurrent.CountDownLatch;
    
    import org.apache.zookeeper.AsyncCallback;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.WatchedEvent;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.ZooKeeper;
    import org.apache.zookeeper.data.Stat;
    import org.apache.zookeeper.Watcher.Event.EventType;
    import org.apache.zookeeper.Watcher.Event.KeeperState;
    import org.apache.zookeeper.ZooDefs.Ids;
    
    public class GetData2 implements Watcher {
        public static CountDownLatch connectedSemaphore = new CountDownLatch(1);
        private static ZooKeeper zk = null;
        private static Stat stat = new Stat();
        
            @Override
            public void process(WatchedEvent event) {
                if (KeeperState.SyncConnected==event.getState()) {
                    if (EventType.None.getIntValue()==event.getState().getIntValue()&&null==event.getPath()) {
                        connectedSemaphore.countDown();
                    }
                }
            }
            public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
                String path = "/zk-book";
                 zk = new ZooKeeper("192.168.64.60:2181", 5000, new GetData2());
                connectedSemaphore.await();
                
                zk.create(path, "123".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL );
                
                zk.setData(path, "456".getBytes(), -1,new IDataback(),"");
                Thread.sleep(Integer.MAX_VALUE);;
    
            }
    }
    
    class IDataback implements AsyncCallback.StatCallback{
    
        @Override
        public void processResult(int rc, String path, Object ctx, Stat stat) {
            if (rc==0) {
                System.out.println("SUCCESS");
            }
            
        }
    
        
    }

    检测节点是否存在

    客户端可以通过zookeeper的api来检测一个节点

     

     该接口主要用于检测指定节点是否存在,返回值是一个Stat对象。如果在调用接口时注册Watcher的话,

    还可以对节点是否存在进行监听,一旦节点被创建被删除或被更新,都会通知客户端。

    权限控制

    在zookeeper的实际使用中,我们的做法往往是搭建一个共用的zookeeper集群,同一为若干个应用提供服务,在这种情况下,不同的应用往往是不存在共享数据的场景的。因此需要解决不同应用之间的权限问题。

    zookeeper提供了多种权限控制模式(Scheme),分别是world,auth,digest,ip和super。

    本节中我们主要讲digest模式下是如何进行zookeeper权限控制的。

    开发人员如果要使用zookeeper的权限控制功能,需要在zookeeper完成会话创建的时候,给该会话添加相关的权限信息(AuthInfo)。zookeeper客户端提供了相应的API接口来进行权限信息的设置。

    该接口的主要作用是为当前的zookeeper会话添加权限信息,之后范式通过该会话对zookeeper服务端进行的任何操作都会带上该权限信息。

    使用包含权限信息的zookeeper会话创建数据节点。

    package auth;
    
    import java.io.IOException;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.ZooDefs.Ids;
    import org.apache.zookeeper.ZooKeeper;
    
    public class AuthInfo {
        
        final static String PATH="/zk-book-auth_test";
        public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
            ZooKeeper zk = new ZooKeeper("192.168.64.60:2181", 5000, null);
            zk.addAuthInfo("digest", "foo:true".getBytes());
            zk.create(PATH, "123".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);
            Thread.sleep(Integer.MAX_VALUE);
        }
    }

     使用无权限信息的zookeeper会话访问权限信息的数据节点。

    package auth;
    
    import java.io.IOException;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.ZooDefs.Ids;
    import org.apache.zookeeper.ZooKeeper;
    
    public class AuthInfo2 {
        
        final static String PATH="/zk-book-auth_test";
        public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
            ZooKeeper zk = new ZooKeeper("192.168.64.60:2181", 5000, null);
            zk.addAuthInfo("digest", "foo:true".getBytes());
            zk.create(PATH, "123".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);
            ZooKeeper zk2 = new ZooKeeper("192.168.64.60:2181", 5000, null);
            zk2.getData(PATH, false, null);
            Thread.sleep(Integer.MAX_VALUE);
        }
    }

     一旦我们对一个数据节点设置了权限信息,那么其他没有权限设置的客户端会话将无法访问该数据节点。

    对于删除及诶单(delete)接口而言,其权限控制比较特殊。

    import org.apache.zookeeper.CreateMode;  
    import org.apache.zookeeper.ZooDefs.Ids;  
    import org.apache.zookeeper.ZooKeeper;  
    /** 
     *  
    * @ClassName: AuthSample_Delete  
    * @Description: TODO(删除节点的权限控制)  
    * @author RongShu 
    * @date 2017年6月11日 下午8:41:23  
    * 
     */  
    public class AuthSample_Delete {  
        final static String PATH  = "/zk-book-auth_test";  
        final static String PATH2 = "/zk-book-auth_test/child";  
        public static void main(String[] args) throws Exception {  
            ZooKeeper zookeeper1 = new ZooKeeper("localhost:2181",5000,null);  
            zookeeper1.addAuthInfo("digest", "foo:true".getBytes());  
            zookeeper1.create( PATH, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT );  
            zookeeper1.create( PATH2, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL );  
            try {  
                ZooKeeper zookeeper2 = new ZooKeeper("localhost:2181",50000,null);  
                zookeeper2.delete( PATH2, -1 );  
            } catch ( Exception e ) {  
                System.out.println( "删除节点失败: " + e.getMessage() );  
            }  
            ZooKeeper zookeeper3 = new ZooKeeper("localhost:2181",50000,null);  
            zookeeper3.addAuthInfo("digest", "foo:true".getBytes());  
            zookeeper3.delete( PATH2, -1 );  
            System.out.println( "成功删除节点:" + PATH2 );  
            ZooKeeper zookeeper4 = new ZooKeeper("localhost:2181",50000,null);  
            zookeeper4.delete( PATH, -1 );  
            System.out.println( "成功删除节点:" + PATH );  
        }  
    }  
      
    输出  
    删除节点失败: KeeperErrorCode = NoAuth for /zk-book-auth_test/child  
    成功删除节点:/zk-book-auth_test/child  
    成功删除节点:/zk-book-auth_test  

    注意:

    当客户端对一个数据节点添加了权限信息后,对于删除操作而言,其作用范围是其子节点,也就是说,当我们对一个数据节点添加权限信息之后,依然可以自由的删除这个节点,但是对于这个节点的子节点,就必须使用相应的权限信息才能够删掉它。

  • 相关阅读:
    js&jquery避免报错的方法
    if-else用法
    js-form表单元素的自定义属性
    a标签
    jQuery知识集锦
    JDK动态代理
    hibernate之多对一单向关联
    STL算法设计理念
    计算机常识--win7 删除文件、拒绝訪问等等,所有提示权限不够 解决的方法
    大话设计模式C++实现-第8章-工厂方法模式
  • 原文地址:https://www.cnblogs.com/duan2/p/9070011.html
Copyright © 2020-2023  润新知