<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          實(shí)戰(zhàn):10 分鐘掌握分布式 ID 之雪花算法

          共 396字,需瀏覽 1分鐘

           ·

          2020-08-22 22:48

          實(shí)戰(zhàn):10 分鐘掌握分布式 ID 之雪花算法

          一個(gè)在生產(chǎn)每天經(jīng)過1億+數(shù)據(jù)量驗(yàn)證的id生成器

          背景

          1.為什么要使用雪花算法生成 ID

          -- 保證 id 全局唯一
          -- 保證 id 自增長
          -- uuid 無序且過長

          雪花算法 ID 組成

          1: 1位標(biāo)識(shí)部分:
          --- 在 java 中由于 long 的最高位是符號(hào)位,正數(shù)是 0,負(fù)數(shù)是 1,一般生成的 ID 為正數(shù),所以為 0;
          2: 41 位時(shí)間戳部分:
          --- 這個(gè)是毫秒級(jí)的時(shí)間,一般實(shí)現(xiàn)上不會(huì)存儲(chǔ)當(dāng)前的時(shí)間戳,而是時(shí)間戳的差值(當(dāng)前時(shí)間-固定的開始時(shí)間),這樣可以使產(chǎn)生的 ID 從更小值開始;41 位的時(shí)間戳可以使用 69 年,(1L<< 41) / (1000L _ 60 _ 60 _ 24 _ 365) = 69 年;
          3: 10 位workid:

          Twitter 實(shí)現(xiàn)中使用前 5 位作為數(shù)據(jù)中心標(biāo)識(shí),后 5 位作為機(jī)器標(biāo)識(shí),可以部署 1024 個(gè)節(jié)點(diǎn)。我這里的實(shí)現(xiàn)根據(jù)服務(wù)名生產(chǎn)的,意思就是說每個(gè)服務(wù)只要不超過 1024 個(gè)節(jié)點(diǎn)就不會(huì)有問題,實(shí)際生產(chǎn)中我也沒有見過某個(gè)服務(wù)有 1024 個(gè)節(jié)點(diǎn)的。

          4: 12 位序列號(hào)部分:
          --- 支持同一毫秒內(nèi)同一個(gè)節(jié)點(diǎn)可以生成 4096 個(gè) ID。意思就是說某個(gè)服務(wù) 1ms 能生成 4096 個(gè) id,如果你單表的 TPS 超過 4096\*60s,那可能就會(huì)出問題了,實(shí)際生產(chǎn)這么大的 TPS 我是沒有見過的。

          Zookeeper生成workid

          雪花算法生成ID網(wǎng)絡(luò)上方法很多,很多重復(fù)的東西我就不贅述了,這里簡(jiǎn)明扼要的說一下ZK生成workid。

          這是生成的ID的截圖。父節(jié)點(diǎn)是workid,只要有引用id生成器jar的服務(wù)都會(huì)在workid下面生成一個(gè)文件夾,以服務(wù)名命名,有多少個(gè)節(jié)點(diǎn)該節(jié)點(diǎn)下就會(huì)有多個(gè)文件,該節(jié)點(diǎn)下的id一定是不會(huì)重復(fù)的。下面我貼一下java版生成workid的核心代碼

          private void buildWorkId(final String appPath) {        // 檢測(cè)client是否已經(jīng)連接上        if (null == client) {            throw new RuntimeException("本節(jié)點(diǎn)注冊(cè)到ZK異常。");        }        // lockPath,用于加鎖,注意要與nodePath區(qū)分開        final String lockPath = this.ROOT_NAME + "/" + this.appName;        // nodePath 用于存放集群各節(jié)點(diǎn)初始路徑        final String nodePath = this.ROOT_NAME + "/" + this.appName + this.NODE_NAME;//         InterProcessMutex 分布式鎖(加鎖過程中l(wèi)ockPath會(huì)自動(dòng)創(chuàng)建)        InterProcessLock interProcessLock = new InterProcessMutex(client, lockPath);        try {            if (!interProcessLock.acquire(5000, TimeUnit.MILLISECONDS)) {                throw new TimeoutException("ZK分布式鎖 加鎖超時(shí),超時(shí)時(shí)間: " + 5000);            }            // nodePath 第一次需初始化,永久保存, 或者節(jié)點(diǎn)路徑為臨時(shí)節(jié)點(diǎn),則設(shè)置為永久節(jié)點(diǎn)            if (null == client.checkExists().forPath(nodePath)) {                client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(nodePath);            }            // 獲取nodePath下已經(jīng)創(chuàng)建的子節(jié)點(diǎn)            List childPath = client.getChildren().forPath(nodePath);            Set nodeIdSet = new LinkedHashSet<>();            if (!CollectionUtils.isEmpty(childPath)) {                for (String path : childPath) {                    try {                        nodeIdSet.add(Integer.valueOf(path));                    } catch (Exception e) {                    	log.warn("路徑由不合法操作創(chuàng)建,注意[" + nodePath + "]僅用于構(gòu)建workId");                    }                }            }            // 遍歷所有id,構(gòu)建workId,主要是判斷可用id是否已經(jīng)被集群中其他節(jié)點(diǎn)占用            for (Integer order : OrderIdSet) {                if (!nodeIdSet.contains(order)) {                    final String currentNodePath = nodePath + "/" + order;                    String nodeDate = String.format("[ip:%s,hostname:%s,pid:%s]",                            InetAddress.getLocalHost().getHostAddress(),                            InetAddress.getLocalHost().getHostName(),                            ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);                    try {                    	client.create().withMode(CreateMode.EPHEMERAL).forPath(currentNodePath, nodeDate.getBytes("UTF-8"));                    } catch (Exception e) {                    	log.debug("節(jié)點(diǎn)[{}]無法創(chuàng)建,可能是已存在", currentNodePath);						continue;					}                    long pathCreateTime = client.checkExists().forPath(currentNodePath).getCtime();                    // 以下邏輯主要用于檢測(cè)斷開重連情況                    TreeCache treeCache = new TreeCache(client, currentNodePath);                    // 添加監(jiān)聽器                    treeCache.getListenable().addListener(new TreeCacheListener() {                        public void childEvent(CuratorFramework curatorFramework,                                               TreeCacheEvent treeCacheEvent) throws Exception {                            long pathTime;                            try {                                pathTime = curatorFramework.checkExists().forPath(currentNodePath).getCtime();                            } catch (Exception e) {                                pathTime = 0;                            }                            // 如果pathTime != pathCreateTime, 那么只能一種情況:                            // 當(dāng)前應(yīng)用與zk失去聯(lián)系,且/nodePath/{currentNodePath}不存在或者被其它應(yīng)用占據(jù)了(表象為pathCreateTime變化)                            // 無論哪種情況,當(dāng)前應(yīng)用都要重新注冊(cè)節(jié)點(diǎn)                            if (pathCreateTime != pathTime) {                            	log.info("從ZK斷開,再次注冊(cè)...");                                // 關(guān)閉之前舊的treeCache                                try {                                    treeCache.close();                                } catch (Exception e) {                                	log.warn("treeCache關(guān)閉失敗");                                }                                // 再次注冊(cè)                                finally {                                    buildWorkId(appPath);                                }                            }                        }                    });                    treeCache.start();                    workerId = order;                    log.info("基于ZK成功構(gòu)建 workId:{}", workerId);                    return;                }            }                    } catch (Exception e) {        	e.printStackTrace();        	log.error("獲取分布式WorkId異常", e);        } finally {            // 構(gòu)建成功后釋放鎖            if (interProcessLock != null) {                try {                    interProcessLock.release();                } catch (Exception e) {                	log.warn("釋放鎖失敗");                }            }        }    }

          核心代碼我已經(jīng)貼出來了,對(duì)雪花算法有一定了解的同學(xué),使用的時(shí)候在需要id生成器的地方引用就好了,還有部分非核心代碼,這個(gè)ID生成器已經(jīng)在生產(chǎn)驗(yàn)證了每天1億+的數(shù)據(jù)量,如果你真的需要可以聯(lián)系我,把代碼都給你。zookeeper其實(shí)是一個(gè)很實(shí)用的工具,還有分布式鎖的實(shí)現(xiàn)及應(yīng)用,下篇文章會(huì)給大家?guī)碛脄k生成分布式鎖。

          END

          喜歡請(qǐng)掃碼關(guān)注

          - END -點(diǎn)擊原文獲取電商實(shí)戰(zhàn)落地項(xiàng)目視頻資料



          瀏覽 96
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  WWW.成人大香蕉网 | 樱桃香蕉视频 | 91狠狠综合 | 激情成人开心网 | 无码123 |