zookeeper的命名服務有兩個應用方向,一個是提供相似JNDI的功能,利用zookeepeer的樹型分層結構,能夠把系統中各類服務的名稱、地址以及目錄信息存放在zookeeper,須要的時候去zookeeper中讀取。node
另外一個,是利用zookeeper順序節點的特性,製做分佈式的ID生成器,寫過數據庫應用的朋友都知道,咱們在往數據庫表中插入記錄時,一般須要爲該記錄建立惟一的ID,在單機環境中咱們能夠利用數據庫的主鍵自增功能。但在分佈式環境則沒法使用,有一種方式能夠使用UUID,可是它的缺陷是沒有規律,很難理解。利用zookeeper順序節點的特性,咱們能夠生成有順序的,容易理解的,同時支持分佈式環境的序列號。數據庫
public class IdMaker { private ZkClient client = null; private final String server; // zookeeper順序節點的父節點 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; } } // 檢測當前服務是否正在運行 private void checkRunning() throws Exception { if (!running) throw new Exception("請先調用start"); } // 從順序節點名中提取咱們要的ID值 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 public String generateId(RemoveMethod removeMethod) throws Exception{ checkRunning(); // 構造順序節點的完整路徑 final String fullNodePath = root.concat("/").concat(nodeName); // 建立持久化順序節點 final String ourPath = client.createPersistentSequential(fullNodePath, null); // 避免zookeeper的順序節點暴增,直接刪除掉剛建立的順序節點 if (removeMethod.equals(RemoveMethod.IMMEDIATELY)){ // 當即刪除 client.delete(ourPath); }else if (removeMethod.equals(RemoveMethod.DELAY)){ // 延遲刪除 cleanExector.execute(new Runnable() { // 用線程池執行刪除,讓generateId()方法儘快返回 public void run() { client.delete(ourPath); } }); } //node-0000000000, node-0000000001 return ExtractId(ourPath); } }
public class TestIdMaker { public static void main(String[] args) throws Exception { IdMaker idMaker = new IdMaker("192.168.1.105:2181", "/NameService/IdGen", "ID"); idMaker.start(); try { for (int i = 0; i < 10; i++) { String id = idMaker.generateId(RemoveMethod.DELAY); System.out.println(id); } } finally { idMaker.stop(); } } }