在微服務系統中,系統的業務邏輯會分佈在不一樣的服務中,不一樣的服務也能夠起多個,那麼如何標識每個服務,須要對每個服務起一個惟一id,該id須要具備惟一性,以及知足必定的順序性,例如按照服務啓動的順序生成服務id號,若是每一個服務都使用了雪花算法,雪花算法中的節點id如何生成?實現方案有4種。java
配置文件
在配置文件中增長一個服務nodeId配置(簡單粗暴,不推薦,萬不得已才使用)node
application: #別的服務配置其餘nodeId nodeId: 1
使用隨機數
當須要生成惟一id,在必定返回內,生成隨機數,例如在1-1024中生成隨機數。算法
return new Random().nextInt(1024) + 1;
獲取當前服務所在機器的mac地址,對mac地址進行位運算(若是一個機器部署多個服務,就會有問題了)apache
static int getNodeId() { InetAddress ip = InetAddress.getLocalHost(); NetworkInterface network = NetworkInterface.getByInetAddress(ip); int id; if (network == null) { log.info("network is [{}]",network) } else { byte[] mac = network.getHardwareAddress(); id = ((0x000000FF & (int) mac[mac.length - 1]) | (0x0000FF00 & (((int) mac[mac.length - 2]) << 8))) >> 6; } return id; }
使用zookeeper臨時節點(推薦)
首先創建一個zk持久節點,每個服務啓動時在該節點下創建一個臨時節點,若是服務中止了,臨時節點也會中止。數組
package com.xiayu.config; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex; import org.apache.curator.framework.recipes.locks.Locker; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.IntStream; @Getter @Slf4j public class Node { static final Logger LOG = LoggerFactory.getLogger(Node.class); private final int nodeId; //生成的節點id private final static int MAX_NODE_NUM = 1024; //節點數目最大值 final private String nonReentrantLockPath = "/application/lock/nonreentrant"; final private String nodePath = "/application/nodes"; //節點目錄 static final private String fullNodePrefix = "/application/nodes/node"; //節點下的臨時節點 static final private String nodePrefix = "node"; //臨時節點前綴 final private InterProcessSemaphoreMutex interProcessSemaphoreMutex; final private CuratorFramework client; //zk 客戶端 public Node(CuratorFramework client) throws Exception { this.client = client; interProcessSemaphoreMutex = new InterProcessSemaphoreMutex(client, "/application/lock/nonreentrant"); this.nodeId = generateNodeIdId(); } static byte[] getData() {//臨時節點下的數據 String ip = "0"; try { ip = InetAddress.getLocalHost().getHostAddress(); } catch (Exception ex) { } return (ip + "," + ManagementFactory.getRuntimeMXBean().getName()).getBytes(StandardCharsets.UTF_8); } int generateNodeIdId() throws Exception { try (Locker locker = new Locker(interProcessSemaphoreMutex, 2, TimeUnit.MINUTES)) { //可重入鎖 Stat exist = this.client.checkExists().forPath(nodePath); //服務節點目錄 if (exist == null) { //服務節點目錄不存在就建立 this.client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(nodePath); } //服務節點目錄下的全部臨時節點 List<String> nodes = this.client.getChildren().usingWatcher((Watcher) event -> LOG.info("Got event [{}]", event)).forPath(nodePath); LOG.info("got temp nodes [{}]", nodes); //找到服務節點中,數目最大的節點 List<Integer> existsIds = nodes.stream() .filter(x -> x.startsWith(nodePrefix)) //過濾 .map(x -> x.substring(nodePrefix.length())) //先截取掉 fullWorkerNodePrefix 獲得後面的數字 //如/application/nodes/node45 獲取45 .map(Integer::parseInt) .collect(Collectors.toList()); //獲取list if (existsIds.size() >= MAX_NODE_NUM) { //若是數組數目已經大於最大值,那麼服務將起不了了 throw new IllegalStateException("Max " + MAX_NODE_NUM + " nodeId reached, Cannot start new instance"); } int max = existsIds.stream().max(Integer::compareTo).orElse(-1); //找到數組中的最大值 int nextId = max + 1; //在1到最大值加1中找到一個隨機值,而且不能已經存在,這樣作是由於臨時節點隨時可能消失 if (existsIds.size() != nextId) { nextId = IntStream.range(0, nextId) .filter(x -> !existsIds.contains(x)) .findFirst().orElse(nextId); } assert !existsIds.contains(nextId) : "new node id should not in zk path " + nodePath; //建立新生成的節點 String nextNodePath = this.client.create() .creatingParentsIfNeeded() .withMode(CreateMode.EPHEMERAL) .forPath(fullNodePrefix + nextId, getData()); return nextId; //返回服務id } } }
採用zookeeper方案是比較推薦的,可是zk的方案服務的最大數目是1024,對絕大數項目都知足了,可是若是在某種狀況下,若是zookeeper沒有起來,可是服務還要啓動,就能夠考慮mac地址方案、隨機數方案和讀取配置文件了,方案推薦的順序爲zk>mac>random>config;在實際項目中,能夠融合多種方案,保證高可用。app