系统唯一ID生成分案有很多种,例如:数据库 auto_increment,UUID,Redis生成ID(Redis原子操作INCR和INCRBY),Twiitter的snowflake算法,ZooKeeper生成ID,MongoDb的ObjectId,下面我们就看一下ZooKeeper实现分布式系统唯一ID。

具体步骤:1.创建持久的顺序节点
              2.获取返回的节点名称,提取ID
              3.根据需要删除节点

代码实现:

package com.tlk.zk.chapter5.Id;

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;

/**
 * Id生成器
 * 另外如果换了新的ZooKeeper,Id生成器就会从0开始,这是ZooKeeper生成Id的弊端
 * @author tanlk
 * @date 2017年8月13日 下午5:10:43
 */
public class IdMaker {
	
	private ZkClient client = null;
	private final String server;//记录服务器的地址
	private final String root;//记录父节点的路径
	private final String nodeName;//节点的名称
	private volatile boolean running = false;
	private ExecutorService cleanExector = null;
	
	//删除节点的级别
	public enum RemoveMethod{
		NONE,IMMEDIATELY,DELAY
		
	}
	
	public IdMaker(String zkServer,String root,String nodeName){
		
		this.root = root;
		this.server = zkServer;
		this.nodeName = nodeName;
		
	}
	
	public void start() throws Exception {
		
		if (running)
			throw new Exception("server has stated...");
		running = true;
		
		init();
		
	}
	
	
	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){
			//ignore;
		}
		
	}
	
	/**
	 * 释放服务资源
	 */
	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;
			
		}
	}
	
	/**
	 * 检测服务是否正在运行
	 * @throws Exception
	 */
	private void checkRunning() throws Exception {
		if (!running)
			throw new Exception("请先调用start");
		
	}
	
	private String ExtractId(String str){
		int index = str.lastIndexOf(nodeName);
		if (index >= 0){
			index+=nodeName.length();
			return index <= str.length()?str.substring(index):"";
		}
		return str;
		
	}
	
	/**
	 * 产生ID
	 * 核心函数
	 * @param removeMethod 删除的方法
	 * @return
	 * @throws Exception
	 */
	public String generateId(RemoveMethod removeMethod) throws Exception{
		checkRunning();
		final String fullNodePath = root.concat("/").concat(nodeName);
		//返回创建的节点的名称
		//final String ourPath = client.createPersistentSequential(fullNodePath, null);
		final String ourPath = client.createEphemeralSequential(fullNodePath, null);
		
		System.out.println(ourPath);
		
		/**
		 * 在创建完节点后为了不占用太多空间,可以选择性删除模式
		 */
		if (removeMethod.equals(RemoveMethod.IMMEDIATELY)){
			client.delete(ourPath);
		}else if (removeMethod.equals(RemoveMethod.DELAY)){
			cleanExector.execute(new Runnable() {
				
				public void run() {
					// TODO Auto-generated method stub
					client.delete(ourPath);
				}
			});
			
		}
		//node-0000000000, node-0000000001,ExtractId提取ID
		return ExtractId(ourPath);
	}

}


package com.tlk.zk.chapter5.Id;

import com.tlk.zk.chapter5.Id.IdMaker.RemoveMethod;

/**
 * 测试类
 * @author tanlk
 * @date 2017年8月13日 下午5:10:57
 */
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 < 10; i++) {
				String id = idMaker.generateId(RemoveMethod.NONE);
				System.out.println(id);

			}
		} finally {
			idMaker.stop();

		}
	}

}



PS:感觉有点恶心的是,换了ZooKeeper,数据就要从0开始,还没有直接可以修改指定数字那里开始,只能写程序一点点的创建,直到达到你要想的数据



Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐