Apache Curator简单使用(三)

来源:互联网 发布:app软件制作教程 编辑:程序博客网 时间:2024/05/23 00:02
转载自:http://www.chengxuyuans.com/Java+/72042.html

分布式锁Lock

       Curator中的支持的锁服务有多种类型,详见http://ifeve.com/zookeeper-lock/。
       共享锁: 全局同步分布式锁, 同一时间两台机器只有一台能获得同一把锁。
       共享读写锁: 用于分布式的读写互斥处理, 同时生成两个锁:一个读锁, 一个写锁, 读锁能被多个应用持有, 而写锁只能一个独占, 当写锁未被持有时, 多个读锁持有者可以同时进行读操作。
       共享信号量: 在分布式系统中的各个JVM使用同一个zk lock path, 该path将跟一个给定数量的租约(lease)相关联, 然后各个应用根据请求顺序获得对应的lease, 相对来说, 这是最公平的锁服务使用方式。 
       多共享锁:内部构件多个共享锁(会跟一个znode path关联), 在acquire()过程中, 执行所有共享锁的acquire()方法, 如果中间出现一个失败, 则将释放所有已require的共享锁; 执行release()方法时, 则执行内部多个共享锁的release方法(如果出现失败将忽略)。

       如下展示如何设计一个分布式重入锁,其中一个path表示一个锁资源.
public class DistributedLock{    private InterProcessMutex lock;//重入的,排他的.    private Map<Thread,Boolean> lockedThread = new WeakHashMap<Thread,Boolean>();    private String lockPath;    private ConnectionStateListener stateListener = new StateListener();    private RevocationListener<InterProcessMutex> revocationListener;    public DistributedLock(CuratorFramework client,String path){        lockPath = path;        revocationListener = new RevocationListener<InterProcessMutex>() {            @Override            public void revocationRequested(InterProcessMutex forLock) {                if(!forLock.isAcquiredInThisProcess()){                    return;                }                try{                    forLock.release();                }catch(Exception e){                    e.printStackTrace();                }            }        };        lock = createLock(client);        lock.makeRevocable(revocationListener);        client.getConnectionStateListenable().addListener(stateListener);    }    public boolean lock(){        try{            lock.acquire();            lockedThread.put(Thread.currentThread(),Boolean.TRUE);        } catch (Exception e){            //        }        return false;    }    public void unlock(){        try{            lock.release();        }catch (Exception e){            //        }    }    private InterProcessMutex createLock(CuratorFramework client){        lock = new InterProcessMutex(client,lockPath);        //协同中断,如果其他线程/进程需要此锁中断时,调用此listener.        lock.makeRevocable(revocationListener);        client.getConnectionStateListenable().addListener(stateListener);        return lock;    }    class StateListener implements ConnectionStateListener{        @Override        public void stateChanged(CuratorFramework client, ConnectionState newState) {            if(Boolean.FALSE.equals(lockedThread.get(Thread.currentThread()))){                return;//如果当前lock没有获取锁,则忽略            }            switch (newState){                case LOST:                    //一旦丢失链接,就意味着zk server端已经删除了锁数据                    lockedThread.clear();                    lock = createLock(client);//must be rebuild                    break;                default:                    System.out.println(newState.toString());            }        }    }}
       底层的机制非常的简单: "获取锁"的操作,就是在zookeeper中创建一个EPHEMERAL_SEQUENTIAL节点,同时对此节点的临近节点注册一个watcher; 当"临近节点"被删除时,表示其他进程已经释放了锁,此watcher将会触发,并唤醒当前线程,然后acquire方法返回.."释放锁"的操作,就是 删除此临时节点.此时临近的下一个节点将获得锁。
       所谓"重入",就是同一个线程多次获取锁时,如果此线程已经持有了锁(即创建了zk临时节点),事实上将不会再次创建zk的临时节点,而是直接返回。
       因为"重入锁",基于临时节点的特性,因此必须关注client链接重建的问题;粗糙的解决办法,就是每次链接重建(session过期),重新实例化lock对象。

Barrier

       栅栏, 可以用来协同分布式环境中的线程.让他们有条件的阻塞,且同时唤醒.
DistributedBarrier barrier = new DistributedBarrier(client,"/barrier");barrier.setBarrier(); //设置barrierSystem.out.println("setBarrier...");barrier.waitOnBarrier();//等待其他进程移除barrier,此后所有的waitOnBarrier进程都将解除阻塞.//barrier.removeBarrier(); //移除barrier,解除阻塞.
DistributedDoubleBarrier barrier = new DistributedDoubleBarrier(client,"/d-barrier",12);System.out.println("enter...");barrier.enter();//阻塞,直到12个成员加入System.out.println("running...");barrier.leave();//阻塞,直到12个成员离开
       其中DistributedDoubleBarrier为双端栅栏,可以让N个线程(进程)同时开始,并且同时退出。对于DistributedBarrier内部机制非常简单: setBarrier()方法就是创建"栅栏"节点,removeBarrier()方法就是删除此节点;当执行setBarrier之后,所有的 waitOnBarrier()操作都将阻塞,直到删除节点的事件触发。DistributedBarrier 会监控连接状态,当连接断掉时waitOnBarrier()方法会抛出异常。


leader选举

       Curator实现Leader选举有两种方式,第一种是LeaderLatch,这种是有阻塞的,所有client一起去争leader,没有选上的client会一直阻塞,等待上一leader退出或则挂掉。一旦leader位置为空了,继续争夺leader。第二种是LeaderSelector监听器实现Leader选举功能。同一时刻,只有一个Listener会进入takeLeadership()方法,说明它是当前的Leader。注意:当Listener从takeLeadership()退出时就说明它放弃了“Leader身份”, 这时Curator会利用Zookeeper再从剩余的Listener中选出一个新的Leader。autoRequeue()方法使放弃 Leadership的Listener有机会重新获得Leadership,如果不设置的话放弃了的Listener是不会再变成Leader的。
       LeaderLatch示例:
        // 选举Leader 启动        LeaderLatch latch = new LeaderLatch(client, "/leader");        latch.addListener(new LeaderLatchListener(){            @Override            public void isLeader() {                System.out.println("I am leader");            }            @Override            public void notLeader() {                System.out.println("I not leader");            }        });        latch.start();//        latch.await();//        System.out.println("I am leader");        System.out.println(latch.hasLeadership());        TimeUnit.SECONDS.sleep(20);        latch.close();
       latch会参与竞选leader,如果被选举为leader,会调用isLeader()方法,hasLeadership()会返回true。

       LeaderSelector示例:
        final LeaderSelector leaderSelector = new LeaderSelector(client, "/leader",                new LeaderSelectorListenerAdapter() {                    @Override                    public void takeLeadership(CuratorFramework client) throws Exception {                        System.out.println("I am leader, working...");                        TimeUnit.SECONDS.sleep(3);                        System.out.println("I am leader, end.");                    }                });        leaderSelector.autoRequeue();        leaderSelector.start();        TimeUnit.SECONDS.sleep(1000);
       除了LeaderSelectorListenerAdapter外,还可以使用LeaderSelectorListener,LeaderSelectorListener将会额外监控stateChanged。
        LeaderSelectorListener selectorListener = new LeaderSelectorListener() {            //此方法将会在Selector的线程池中的线程调用            @Override            public void takeLeadership(CuratorFramework client) throws Exception {                System.out.println("I am leader...");                //如果takeLeadership方法被调用,说明此selector实例已经为leader                //此方法需要阻塞,直到selector放弃leader角色            }            //这个方法将会在Zookeeper主线程中调用---watcher响应时            @Override            public void stateChanged(CuratorFramework client, ConnectionState newState) {                System.out.println("Connection state changed...");                //对于LeaderSelector,底层实现为对leaderPath节点使用了"排他锁",                //"排他锁"的本质,就是一个"临时节点"                //如果接收到LOST,说明此selector实例已经丢失了leader信息.                if (newState == ConnectionState.LOST) {                    //需要做特殊处理                }            }        };
0 0
原创粉丝点击