zookeepeer ID生成器 (一)
目錄
瘋狂創客圈 Java 分散式聊天室【 億級流量】實戰系列之 -25【 部落格園 總入口 】
寫在前面
大家好,我是作者尼恩。目前和幾個小夥伴一起,組織了一個高併發的實戰社群【瘋狂創客圈】。正在開始高併發、億級流程的 IM 聊天程式 學習和實戰
前面,已經完成一個高效能的 Java 聊天程式的四件大事:
接下來,需要進入到分散式開發的環節了。 分散式的中介軟體,瘋狂創客圈的小夥伴們,一致的選擇了zookeeper,不僅僅是由於其在大資料領域,太有名了。更重要的是,很多的著名框架,都使用了zk。
本篇介紹 ZK 的分散式命名服務 中的 分散式ID生成器。
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 面試題 一網打盡
瘋狂創客圈 【 部落格園 總入口 】