Zookeeper应用场景之分布式屏障Barrier

Barrier就是栅栏或者屏障,适用于这样的业务场景:当有些操作需要并行执行,但后续操作又需要串行执行,此时必须等待所有并行执行的线程全部结束,才开始串行,于是就需要一个屏障,来控制所有线程同时开始,并等待所有线程全部结束。

下面放上一个简陋的图以便理解。

要解决的问题如下:

1.如何控制所有线程同时开始?
所有的线程启动时在zookeeper节点/barrier下插入顺序临时节点,然后检查/barrier下所有children节点的数量是否为所有的线程数,如果不是,则等待。
如果是,则开始执行。
2.如何等待所有线程结束?
所有线程在执行完毕后,都检查/barrier下所有children节点数量是否为0,若不为0,则继续等待。
3.用什么类型的节点?
根节点使用持久节点(persistent node),子节点使用临时节点(Ephemeral node)
根节点为什么要用持久节点?首先因为临时节点不能有子节点,所以根节点要用持久节点,并且在程序中要判断根节点是否存在。
子节点为什么要用临时节点?临时节点随着连接的断开而消失,在程序中,虽然会删除临时节点,但可能会出现程序在节点被删除之前就crash了,如果是持久节点,节点不会被删除。
 
首先我写了一个ZookeeperClient,由这个client端负维护一个Zookeeper对象,并对Zookeeper节点进行操作。
public class ZookeeperClient {
    public static ZooKeeper zooKeeper;
    public static final String IP_ADDRESS="xxxx:2181";
    public static void init() throws  Exception{

        zooKeeper = new ZooKeeper(IP_ADDRESS, 15000, new Watcher() {

            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getState()== Event.KeeperState.SyncConnected) {


                }
            }

        });

    }
    public static String createTempNode(String path,String data) {
        try {
            String node = zooKeeper.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            System.out.println("node "+ path +" with data is created,return node "+node);
            return node;
        } catch (KeeperException e) {
            e.printStackTrace();
            return "ERROR";
        } catch (InterruptedException e) {
            e.printStackTrace();
            return "ERROR";
        }
    }
    public  static boolean delete(String path,int version) {
        try {
            zooKeeper.delete(path,version);
            System.out.println("delete path:"+ path + "success");
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        } catch (KeeperException e) {
            e.printStackTrace();
            return false;
        }

    }
    public static boolean createPersistentNode(String path,String data) {
        try {
            String node = zooKeeper.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println("node "+ path +" with data is created");
            return true;
        } catch (KeeperException e) {
            e.printStackTrace();
            return false;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }
    public static boolean checkExists(String path){
        try {
            Stat stat = zooKeeper.exists(path,true);
            if(stat!=null) {
                return true;
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
    public static int getChildrens(String path) {
        try {
            return zooKeeper.getChildren(path,true).size();
        } catch (KeeperException e) {
            e.printStackTrace();
            return -1;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return  -1;
        }
    }
}

Barrier类,负责实现屏障的功能

public class Barrier {
    private int size;
    private String rootPath;
    public Barrier(int size,String rootPath){
        this.rootPath = rootPath;
        this.size = size;


    }
    public void init() throws Exception {
       ZookeeperClient.init();
        if(!ZookeeperClient.checkExists(rootPath)){
            ZookeeperClient.createPersistentNode(rootPath,"1");
        }
    }
    public boolean enter(String name,String number){
        ZookeeperClient.createTempNode(rootPath+"/"+name, number);
        //如果节点下children的数量没有达到所有线程的总数,则继续轮询。
        //此时要等待所有的线程都在根节点下创建了节点,才开始执行
        while(true) {
            int size=ZookeeperClient.getChildrens(rootPath);
            if (size != this.size) {
                try {

                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                return true;
            }
        }
    }
    public  boolean exit(String name){
        //先删除自己的节点
        ZookeeperClient.delete(rootPath+"/"+name,0);
        //如果节点下children数量大于0,则继续轮询
        //此时要等待所有的线程都删除了节点,即所有线程都做完了该做的事情,才结束线程。确保所有的线程同时结束。
        while(true){
            int size = ZookeeperClient.getChildrens(rootPath);
            if(size!=0) {
                System.out.println("The current children node under "+rootPath+" is " + size+", still need waiting");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                return true;
            }
        }
    }
}

BarrierDemo类,负责启动线程,为了模拟等待,我为3个线程设置了不同的休眠时间。

预想的结果是t3首先删除节点,此时子节点剩下两个(t1和t2),t3不会结束,而是继续轮询。

t2随后删除节点,此时子节点剩下1个(t1),t2和t3继续轮询。

t3最后删除节点,此时没有子节点,t1,t2,t3全部结束。

public class BarrierDemo {
    public static void main(String[] args) throws  Exception{
        Barrier barrier = new Barrier(3,"/barrier");
        barrier.init();
        Worker worker1 = new Worker(barrier,10000);
        Worker worker2 = new Worker(barrier,5000);
        Worker worker3 = new Worker(barrier,2000);
        Thread t1 = new Thread(worker1,"t1");
        Thread t2 = new Thread(worker2,"t2");
        Thread t3 = new Thread(worker3,"t3");
        t1.start();
        t2.start();
        t3.start();


    }
}
class Worker implements Runnable {
    Barrier barrier;
    long time;
    Worker(Barrier barrier, long time){
        this.barrier = barrier;
        this.time = time;

    }
    public void run() {
        boolean isEnter=barrier.enter(Thread.currentThread().getName(),"0");
        if(isEnter) {
            System.out.println(Thread.currentThread().getName()+"is working on something important now");
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        boolean isExit=barrier.exit(Thread.currentThread().getName());
        if (isExit) {
            System.out.println(Thread.currentThread().getName()+"is exiting..");
        }
    }
}

运行结果如下

 

 

posted on 2017-10-15 11:46  qingfei  阅读(1422)  评论(0编辑  收藏  举报