Apache Curator简单使用(三)

本文介绍了Zookeeper通过Curator框架实现的几种分布式协调服务,包括分布式锁、栅栏及Leader选举等。分布式锁涵盖共享锁、共享读写锁等多种类型,并详细展示了分布式重入锁的设计与实现。栅栏服务允许进程有条件地阻塞并同时唤醒,而Leader选举则提供了两种实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自: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(); //设置barrier
System.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) {
                    //需要做特殊处理
                }
            }
        };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值