目錄html
瘋狂創客圈 Java 分佈式聊天室【 億級流量】實戰系列之 -25【 博客園 總入口 】java
你們好,我是做者尼恩。目前和幾個小夥伴一塊兒,組織了一個高併發的實戰社羣【瘋狂創客圈】。正在開始高併發、億級流程的 IM 聊天程序 學習和實戰node
前面,已經完成一個高性能的 Java 聊天程序的四件大事:面試
接下來,須要進入到分佈式開發的環節了。 分佈式的中間件,瘋狂創客圈的小夥伴們,一致的選擇了zookeeper,不只僅是因爲其在大數據領域,太有名了。更重要的是,不少的著名框架,都使用了zk。算法
本篇介紹 ZK 的分佈式命名服務 中的 分佈式ID生成器。sql
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節點動態命名。
在分佈式系統中,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是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的方案,是最爲合適的解決方案之一。
經過建立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 算法 。
Java (Netty) 聊天程序【 億級流量】實戰 開源項目實戰
瘋狂創客圈 【 博客園 總入口 】