在微服务系统中,系统的业务逻辑会分布在不一样的服务中,不一样的服务也能够起多个,那么如何标识每个服务,须要对每个服务起一个惟一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