package com.central.common.utils; import cn.hutool.core.date.SystemClock; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.StringPool; import lombok.extern.slf4j.Slf4j; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.concurrent.ThreadLocalRandom; /** * 分布式高效有序ID生成器 * 优化开源项目:http://git.oschina.net/yu120/sequence * * Twitter_Snowflake
* SnowFlake的结构如下(每部分用-分开):
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - * 000000000000
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T * = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
* 加起来刚好64位,为一个Long型。
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 * * @author zlt * @date 2019/3/5 */ @Slf4j public class Sequence { /** * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) */ private final long twepoch = 1288834974657L; /** * 机器标识位数 */ private final long workerIdBits = 5L; private final long datacenterIdBits = 5L; private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /** * 毫秒内自增位 */ private final long sequenceBits = 12L; private final long workerIdShift = sequenceBits; private final long datacenterIdShift = sequenceBits + workerIdBits; /** * 时间戳左移动位 */ private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final long sequenceMask = -1L ^ (-1L << sequenceBits); private final long workerId; /** * 数据标识 ID 部分 */ private final long datacenterId; /** * 并发控制 */ private long sequence = 0L; /** * 上次生产 ID 时间戳 */ private long lastTimestamp = -1L; /** * 时间回拨最长时间(ms),超过这个时间就抛出异常 */ private long timestampOffset = 5L; public Sequence() { this.datacenterId = getDatacenterId(maxDatacenterId); this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); } /** *

* 有参构造器 *

* * @param workerId 工作机器 ID * @param datacenterId 序列号 */ public Sequence(long workerId, long datacenterId) { Assert.isFalse(workerId > maxWorkerId || workerId < 0, String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0, String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); this.workerId = workerId; this.datacenterId = datacenterId; } /** *

* 获取 maxWorkerId *

*/ protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { StringBuilder mpid = new StringBuilder(); mpid.append(datacenterId); String name = ManagementFactory.getRuntimeMXBean().getName(); if (StrUtil.isNotEmpty(name)) { /* * GET jvmPid */ mpid.append(name.split(StringPool.AT)[0]); } /* * MAC + PID 的 hashcode 获取16个低位 */ return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); } /** *

* 数据标识id部分 *

*/ protected static long getDatacenterId(long maxDatacenterId) { long id = 0L; try { InetAddress ip = InetAddress.getLocalHost(); NetworkInterface network = NetworkInterface.getByInetAddress(ip); if (network == null) { id = 1L; } else { byte[] mac = network.getHardwareAddress(); if (null != mac) { id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; id = id % (maxDatacenterId + 1); } } } catch (Exception e) { log.warn(" getDatacenterId: " + e.getMessage()); } return id; } /** * 获取下一个ID * * @return */ public synchronized long nextId() { long timestamp = timeGen(); //闰秒 if (timestamp < lastTimestamp) { long offset = lastTimestamp - timestamp; if (offset <= timestampOffset) { try { wait(offset << 1); timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset)); } } catch (Exception e) { throw new RuntimeException(e); } } else { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset)); } } if (lastTimestamp == timestamp) { // 相同毫秒内,序列号自增 sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { sequence = ThreadLocalRandom.current().nextLong(1, 101); // 同一毫秒的序列数已经达到最大 timestamp = tilNextMillis(lastTimestamp); } } else { // 不同毫秒内,序列号置为 1 - 100 随机数 sequence = ThreadLocalRandom.current().nextLong(1, 101); } lastTimestamp = timestamp; // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分 return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } protected long timeGen() { return SystemClock.now(); } }