ZooKeeper 实现命名服务(分布式的ID生成器)

1、生成器类

package com.zk.zkclient.nameservice;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNodeExistsException;
import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Zookeeper的命名服务(ID生成器)
 *
 * @version 2016年11月29日上午9:51:37
 * @author wuliu
 */
public class IdMaker {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    private ZkClient client = null;
    // 服务地址
    private final String server;
    // id生成器根节点
    private final String root;
    // id节点
    private final String nodeName;
    // 启动状态: true:启动;false:没有启动,默认没有启动
    private volatile boolean running = false;
    private ExecutorService cleanExector = null;
    
    public enum RemoveMethod {
        // 不,立即,延期
        NONE, IMMEDIATELY, DELAY
    }
    
    public IdMaker(String zkServer, String root, String nodeName) {
        this.server = zkServer;
        this.root = root;
        this.nodeName = nodeName;
    }
    
    /**
     * 启动
     *
     * @version 2016年11月29日上午9:37:36
     * @author wuliu
     * @throws Exception
     */
    public void start() throws Exception {
        if (running)
            throw new Exception("server has stated...");
        running = true;
        init();
    }
    
    /**
     * 停止服务
     *
     * @version 2016年11月29日上午9:45:38
     * @author wuliu
     * @throws Exception
     */
    public void stop() throws Exception {
        if (!running)
            throw new Exception("server has stopped...");
        running = false;
        freeResource();
    }
    
    private void init() {
        client = new ZkClient(server, 5000, 5000, new BytesPushThroughSerializer());
        cleanExector = Executors.newFixedThreadPool(10);
        try {
            client.createPersistent(root, true);
        }
        catch (ZkNodeExistsException e) {
            logger.info("节点已经存在,节点路径:" + root);
        }
        
    }
    
    /**
     * 资源释放 T
     *
     * @version 2016年11月29日上午9:38:59
     * @author wuliu
     */
    private void freeResource() {
        cleanExector.shutdown();
        try {
            cleanExector.awaitTermination(2, TimeUnit.SECONDS);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            cleanExector = null;
        }
        if (client != null) {
            client.close();
            client = null;
        }
    }
    
    /**
     * 判断是否启动服务
     *
     * @version 2016年11月29日上午9:39:58
     * @author wuliu
     * @throws Exception
     */
    private void checkRunning() throws Exception {
        if (!running)
            throw new Exception("请先调用start启动服务");
    }
    
    /**
     * 提取ID
     *
     * @version 2016年11月29日上午9:46:48
     * @author wuliu
     * @param str
     * @return
     */
    private String ExtractId(String str) {
        int index = str.lastIndexOf(nodeName);// 20
        if (index >= 0) {
            index += nodeName.length();
            return index <= str.length() ? str.substring(index) : "";
        }
        return str;
    }
    
    /**
     * 获取id
     *
     * @version 2016年11月29日上午9:40:33
     * @author wuliu
     * @param removeMethod
     * @return
     * @throws Exception
     */
    public String generateId(RemoveMethod removeMethod) throws Exception {
        checkRunning();
        final String fullNodePath = root.concat("/").concat(nodeName);
        // 创建顺序节点每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。
        // 基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,
        // ZooKeeper会自动为给定节点名加上一个数字后缀,作为新的节点名
        final String ourPath = client.createPersistentSequential(fullNodePath, null);
        if (removeMethod.equals(RemoveMethod.IMMEDIATELY)) {// 立即删除
            client.delete(ourPath);
        }
        else if (removeMethod.equals(RemoveMethod.DELAY)) {// 延期删除
            cleanExector.execute(new Runnable() {
                public void run() {
                    client.delete(ourPath);
                }
            });
            
        }
        return ExtractId(ourPath);
    }
    
}

2、测试

package com.zk.zkclient.nameservice;

import com.zk.zkclient.nameservice.IdMaker.RemoveMethod;

public class TestIdMaker {

    public static void main(String[] args) throws Exception {
        
        IdMaker idMaker = new IdMaker("127.0.0.1:2181",
                "/NameService/IdGen", "ID-");
        idMaker.start();

        try {
            for (int i = 0; i < 2; i++) {
                String id = idMaker.generateId(RemoveMethod.DELAY);
                System.out.println(id);
            }
        } finally {
            idMaker.stop();
        }
    }

}

3、介绍

3.1、命名服务(提供名字的服务)

zookeeper的命名服务,有两个应用方向:

            1、提供类似JNDI的功能:利用zookeeper中的树形分层结构,可以把系统中的各种服务的名称,地址以及目录信息存放在zookeeper中,

需要的时候去zookeeper中读取

            2、利用zookeeper中的顺序节点的特性,制作分布式的序列号生成器(ID生成器),(在往数据库查询数据时,通常需要一个id,在单机环境下,可以利用数据库的自动成功id号,但是这种在分布式环境下就无法使用了,可以使用UUID,但是UUID有一个缺点,就是没有规律很难理解。使用zookeeper的命名服务可以生成有顺序的容易理解的,支持分布式的编号)

架构图:

ZooKeeper 实现命名服务(分布式的ID生成器)_第1张图片

算法流程图:

ZooKeeper 实现命名服务(分布式的ID生成器)_第2张图片



你可能感兴趣的:(Zookeeper)