zookeepeer ID生成器 (一)

瘋狂創客圈 Java 分佈式聊天室【 億級流量】實戰系列之 -25【 博客園 總入口java


寫在前面

​ 你們好,我是做者尼恩。目前和幾個小夥伴一塊兒,組織了一個高併發的實戰社羣【瘋狂創客圈】。正在開始高併發、億級流程的 IM 聊天程序 學習和實戰node

​ 前面,已經完成一個高性能的 Java 聊天程序的四件大事:面試

接下來,須要進入到分佈式開發的環節了。 分佈式的中間件,瘋狂創客圈的小夥伴們,一致的選擇了zookeeper,不只僅是因爲其在大數據領域,太有名了。更重要的是,不少的著名框架,都使用了zk。算法

本篇介紹 ZK 的分佈式命名服務 中的 分佈式ID生成器。sql

1.1. ZK 的分佈式命名服務

zookeeper的命名服務,主要是利用zookeepeer節點的樹型分層結構和子節點的次序維護能力,爲分佈式系統中的資源命名與標識能力。數據庫

zookeeper的分佈式命名服務,典型的應用場景有:緩存

(1)提供分佈式JNDI的API目錄服務功能。服務器

能夠把系統中各類API接口服務的名稱、連接地址放在zookeeper的樹形分層結果中,提供分佈式的API調用能力。著名的分佈式框架,就是應用了zookeeper的分佈式的JNDI能力。網絡

開源的分佈式服務框架Dubbo中使用ZooKeeper來做爲其命名服務,維護全局的服務接口API地址列表。在Dubbo實現中,provider服務提供者在啓動的時候,向ZK上的指定節點/dubbo/${serviceName}/providers文件夾下寫入本身的API地址,這個操做就至關於服務的公開。

consumer服務消費者啓動的時候,訂閱節點/dubbo/{serviceName}/providers文件夾下的provider服務提供者URL地址,得到全部的訪問提供者的API。

(2)製做分佈式的ID生成器,爲分佈式系統中的每個數據資源,提供的惟一的標識能力。

在單體服務環境下,咱們惟一標識一個數據資源,一般利用數據庫的主鍵自增功能。可是在大量服務器集羣的場景下,依賴單體服務的數據庫主鍵自增生成惟一ID,沒有辦法知足高併發和高負載的需求。

(3)分佈式節點的命名服務

一個分佈式系統會有不少的節點組成,並且,節點的數量是不斷動態變化的。根據業務的膨脹須要和迎接流量洪峯,可能會加入大量的動態不少節點。流量洪峯過去,就須要下線大量的節點。或者說,因爲機器或者網絡的緣由,一些節點主動的離開的集羣。

如何爲大量的動態節點命名呢?一種簡單的辦法是,能夠經過配置文件,手動的進行每個節點的命名。可是若是節點數據量太大,或者說變更頻繁,手動命名是不現實的,這就須要用到分佈式節點的命名服務。

瘋狂創客圈的分佈式IM實戰項目,也會使用分佈式命名服務,爲每個IM節點動態命名。

1.1.1. 分佈式 ID 生成器的類型

在分佈式系統中,ID生成器的使用場景,很是很是多:

(1)大量的數據記錄,須要分佈式ID

(2)大量的系統消息,須要分佈式ID

(3)大量的請求日誌,如http請求記錄,須要惟一標識,以便進行後續的用戶行爲分析和調用鏈路分析,等等等等。

傳統的數據庫自增主鍵,或者單體的自增主鍵,已經不能知足需求。在分佈式系統環境中,迫切須要一個全新的惟一ID的系統,這個系統須要知足如下需求:

(1)全局惟一:不能出現重複ID

(2)高可用:ID生成系統是基礎系統,被許多關鍵系統調用,一旦宕機,會形成嚴重影響。

分佈式惟一ID生成分案有不少種:

(1) java的UUID

(2) 利用分佈式緩存Redis生成ID

利用Redis的原子操做INCR和INCRBY,生成全局惟一的ID。

(3) Twitter的snowflake算法

(4) ZooKeeper生成ID

利用ZooKeeper 的順序節點,生成全局惟一的ID。

(5) MongoDb的ObjectId

利用分佈式Nosql MongDB,生成全局惟一的ID。

首先分析一下java語言中的 UUID方案。

UUID方案

UUID是Universally Unique Identifier的縮寫,它是在必定的範圍內(從特定的名字空間到全球)惟一的機器生成的標識符。UUID在其餘語言中也叫GUID,在java中,生成UUID的代碼很簡單:

String uuid = UUID.randomUUID().toString()

一個UUID是16字節長的數字,一共128位。一般以36字節的字符串表示,好比:3F2504E0-4F89-11D3-9A0C-0305E82C3301。 使用的時候,能夠把中間的4箇中劃線去掉,剩下32位字符串。

UUID經由必定的算法機器生成,爲了保證UUID的惟一性,規範定義了包括網卡MAC地址、時間戳、名字空間(Namespace)、隨機或僞隨機數、時序等元素,以及從這些元素生成UUID的算法。UUID的只能由計算機生成。

UUID的優勢:本地生成ID,不須要進行遠程調用,時延低,性能高。

UUID的缺點:UUID過長,16字節128位,一般以36長度的字符串表示,不少場景不適用,好比,因爲UUID沒有排序,沒法保證趨勢遞增,用作數據庫索引字段的效率就很低,新增記錄存儲入庫時性能差

從高併發,高可用的角度出發,經過ZooKeeper實現分佈式系統惟一ID的方案,是最爲合適的解決方案之一。

1.1.2. ZK生成分佈式ID

經過建立ZK的順序模式的節點,能夠生成全局惟一的ID。

代碼以下:

private String createSeqNode(String pathPefix) {
      try {
            // 建立一個 ZNode 順序節點
            String destPath = client.create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.*EPHEMERAL_SEQUENTIAL*)
//避免zookeeper的順序節點暴增,能夠刪除建立的順序節點
                    .forPath(pathPefix);
            return destPath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

節點建立完成後,會返回節點的完整的層次路徑,生成的序號,放置在路徑的末尾。通常爲10位數字字符。

經過截取路徑末尾的數字,做爲新生成的ID。截取數字的代碼以下:

public String makeId(String nodeName) {
    String str = createSeqNode(nodeName);
    if (null == str) {
        return null;
    }
    int index = str.lastIndexOf(nodeName);
    if (index >= 0) {
        index += nodeName.length();
        return index <= str.length() ? str.substring(index) : "";
    }
    return str;
}

調用的代碼以下:

*/\*** ** create by 尼恩 @ 瘋狂創客圈* **\*/*@Slf4j
public class IDMakerTester {
    @Test
    public void testMakeId() {
        IDMaker idMaker = new IDMaker();
        idMaker.init();
        String nodeName = "/test/IDMaker/ID-";
        for (int i = 0; i < 10; i++) {
            String id = idMaker.makeId(nodeName);
            log.info("第"+ i + "個建立的id爲:" + id);
        }
        idMaker.destroy();
    }
}

下面是部分的運行輸出:

第0個建立的id爲:0000000010

第1個建立的id爲:0000000011

寫在最後

​ 下一篇:基於 zookeeper 實現snowflake 算法 。


瘋狂創客圈 億級流量 高併發IM 實戰 系列

  • Java (Netty) 聊天程序【 億級流量】實戰 開源項目實戰

  • Netty 源碼、原理、JAVA NIO 原理
  • Java 面試題 一網打盡
  • 瘋狂創客圈 【 博客園 總入口 】

相關文章
相關標籤/搜索