001/* 002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.keygen.impl; 017 018import com.mybatisflex.core.exception.FlexExceptions; 019import com.mybatisflex.core.keygen.IKeyGenerator; 020import com.mybatisflex.core.util.StringUtil; 021 022import java.lang.management.ManagementFactory; 023import java.net.InetAddress; 024import java.net.NetworkInterface; 025 026/** 027 * <p>雪花算法 ID 生成器。 028 * 029 * <ul> 030 * <li>最高 1 位固定值 0,因为生成的 ID 是正整数; 031 * <li>接下来 41 位存储毫秒级时间戳,2 ^ 41 / ( 1000 * 60 * 60 * 24 * 365) = 69,大概可以使用 69 年; 032 * <li>再接下 10 位存储机器码,包括 5 位 dataCenterId 和 5 位 workerId,最多可以部署 2 ^ 10 = 1024 台机器; 033 * <li>最后 12 位存储序列号,同一毫秒时间戳时,通过这个递增的序列号来区分,即对于同一台机器而言,同一毫秒时间戳下,可以生成 2 ^ 12 = 4096 个不重复 ID。 034 * </ul> 035 * 036 * <p>优化自开源项目:<a href="https://gitee.com/yu120/sequence">Sequence</a> 037 * 038 * @author 王帅 039 * @since 2023-05-12 040 */ 041public class SnowFlakeIDKeyGenerator implements IKeyGenerator { 042 043 /** 044 * 工作机器 ID 占用的位数(5bit)。 045 */ 046 private static final long WORKER_ID_BITS = 5L; 047 /** 048 * 数据中心 ID 占用的位数(5bit)。 049 */ 050 private static final long DATA_CENTER_ID_BITS = 5L; 051 /** 052 * 序号占用的位数(12bit)。 053 */ 054 private static final long SEQUENCE_BITS = 12L; 055 /** 056 * 工作机器 ID 占用 5bit 时的最大值 31。 057 */ 058 private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); 059 /** 060 * 数据中心 ID 占用 5bit 时的最大值 31。 061 */ 062 private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS); 063 /** 064 * 序号掩码,用于与自增后的序列号进行位“与”操作,如果值为 0,则代表自增后的序列号超过了 4095。 065 */ 066 private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); 067 /** 068 * 工作机器 ID 位需要左移的位数(12bit)。 069 */ 070 private static final long WORK_ID_SHIFT = SEQUENCE_BITS; 071 /** 072 * 数据中心 ID 位需要左移的位数(12bit + 5bit)。 073 */ 074 private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; 075 /** 076 * 时间戳需要左移的位数(12bit + 5bit + 5bit)。 077 */ 078 private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS; 079 /** 080 * 时间起始标记点,一旦确定不能变动(2023-04-02 13:01:00)。 081 */ 082 private static long twepoch = 1680411660000L; 083 /** 084 * 可容忍的时间偏移量。 085 */ 086 private static long offsetPeriod = 5L; 087 088 /** 089 * 工作机器 ID。 090 */ 091 private final long workerId; 092 /** 093 * 数据中心 ID。 094 */ 095 private final long dataCenterId; 096 097 /** 098 * IP 地址信息,用来生成工作机器 ID 和数据中心 ID。 099 */ 100 protected InetAddress address; 101 /** 102 * 同一毫秒内的最新序号,最大值可为(2^12 - 1 = 4095)。 103 */ 104 private long sequence; 105 /** 106 * 上次生产 ID 时间戳。 107 */ 108 private long lastTimeMillis = -1L; 109 110 /** 111 * 雪花算法 ID 生成器。 112 */ 113 public SnowFlakeIDKeyGenerator() { 114 this(null); 115 } 116 117 /** 118 * 根据 IP 地址计算数据中心 ID 和工作机器 ID 生成数据库 ID。 119 * 120 * @param address IP 地址 121 */ 122 public SnowFlakeIDKeyGenerator(InetAddress address) { 123 this.address = address; 124 this.dataCenterId = getDataCenterId(MAX_DATA_CENTER_ID); 125 this.workerId = getWorkerId(dataCenterId, MAX_WORKER_ID); 126 } 127 128 /** 129 * 根据数据中心 ID 和工作机器 ID 生成数据库 ID。 130 * 131 * @param workerId 工作机器 ID 132 * @param dataCenterId 数据中心 ID 133 */ 134 public SnowFlakeIDKeyGenerator(long workerId, long dataCenterId) { 135 if (workerId > MAX_WORKER_ID || workerId < 0) { 136 throw new IllegalArgumentException( 137 String.format("workerId must be greater than 0 and less than %d.", MAX_WORKER_ID)); 138 } 139 if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) { 140 throw new IllegalArgumentException( 141 String.format("dataCenterId must be greater than 0 and less than %d.", MAX_DATA_CENTER_ID)); 142 } 143 this.workerId = workerId; 144 this.dataCenterId = dataCenterId; 145 } 146 147 /** 148 * 根据 MAC + PID 的 hashCode 获取 16 个低位生成工作机器 ID。 149 */ 150 protected long getWorkerId(long dataCenterId, long maxWorkerId) { 151 StringBuilder mpId = new StringBuilder(); 152 mpId.append(dataCenterId); 153 String name = ManagementFactory.getRuntimeMXBean().getName(); 154 if (StringUtil.isNotBlank(name)) { 155 // GET jvmPid 156 mpId.append(name.split("@")[0]); 157 } 158 // MAC + PID 的 hashCode 获取16个低位 159 return (mpId.toString().hashCode() & 0xffff) % (maxWorkerId + 1); 160 } 161 162 /** 163 * 根据网卡 MAC 地址计算余数作为数据中心 ID。 164 */ 165 protected long getDataCenterId(long maxDataCenterId) { 166 long id = 0L; 167 try { 168 if (address == null) { 169 address = InetAddress.getLocalHost(); 170 } 171 NetworkInterface network = NetworkInterface.getByInetAddress(address); 172 if (null == network) { 173 id = 1L; 174 } else { 175 byte[] mac = network.getHardwareAddress(); 176 if (null != mac) { 177 id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6; 178 id = id % (maxDataCenterId + 1); 179 } 180 } 181 } catch (Exception e) { 182 throw FlexExceptions.wrap(e, "dataCenterId: %s", e.getMessage()); 183 } 184 return id; 185 } 186 187 @Override 188 public Object generate(Object entity, String keyColumn) { 189 return nextId(); 190 } 191 192 /** 193 * 获取下一个 ID。 194 */ 195 public synchronized long nextId() { 196 long currentTimeMillis = System.currentTimeMillis(); 197 // 当前时间小于上一次生成 ID 使用的时间,可能出现服务器时钟回拨问题。 198 if (currentTimeMillis < lastTimeMillis) { 199 long offset = lastTimeMillis - currentTimeMillis; 200 // 在可容忍的时间差值之内等待时间恢复正常 201 if (offset <= offsetPeriod) { 202 try { 203 wait(offset << 1L); 204 currentTimeMillis = System.currentTimeMillis(); 205 if (currentTimeMillis < lastTimeMillis) { 206 throw FlexExceptions.wrap("Clock moved backwards, please check the time. Current timestamp: %d, last used timestamp: %d", currentTimeMillis, lastTimeMillis); 207 } 208 } catch (InterruptedException e) { 209 throw FlexExceptions.wrap(e); 210 } 211 } else { 212 throw FlexExceptions.wrap("Clock moved backwards, please check the time. Current timestamp: %d, last used timestamp: %d", currentTimeMillis, lastTimeMillis); 213 } 214 } 215 216 if (currentTimeMillis == lastTimeMillis) { 217 // 相同毫秒内,序列号自增 218 sequence = (sequence + 1) & SEQUENCE_MASK; 219 if (sequence == 0) { 220 // 同一毫秒的序列数已经达到最大 221 currentTimeMillis = tilNextMillis(lastTimeMillis); 222 } 223 } else { 224 // 不同毫秒内,序列号置为 0。 225 sequence = 0L; 226 } 227 228 // 记录最后一次使用的毫秒时间戳 229 lastTimeMillis = currentTimeMillis; 230 231 // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分 232 return ((currentTimeMillis - twepoch) << TIMESTAMP_SHIFT) 233 | (dataCenterId << DATA_CENTER_ID_SHIFT) 234 | (workerId << WORK_ID_SHIFT) 235 | sequence; 236 } 237 238 /** 239 * 获取指定时间戳的接下来的时间戳。 240 */ 241 private long tilNextMillis(long lastTimestamp) { 242 long currentTimeMillis = System.currentTimeMillis(); 243 while (currentTimeMillis <= lastTimestamp) { 244 currentTimeMillis = System.currentTimeMillis(); 245 } 246 return currentTimeMillis; 247 } 248 249 public static long getTwepoch() { 250 return twepoch; 251 } 252 253 public static void setTwepoch(long twepoch) { 254 SnowFlakeIDKeyGenerator.twepoch = twepoch; 255 } 256 257 public static long getOffsetPeriod() { 258 return offsetPeriod; 259 } 260 261 public static void setOffsetPeriod(long offsetPeriod) { 262 SnowFlakeIDKeyGenerator.offsetPeriod = offsetPeriod; 263 } 264 265 public long getWorkerId() { 266 return workerId; 267 } 268 269 public long getDataCenterId() { 270 return dataCenterId; 271 } 272 273 public InetAddress getAddress() { 274 return address; 275 } 276 277 public void setAddress(InetAddress address) { 278 this.address = address; 279 } 280 281 public long getSequence() { 282 return sequence; 283 } 284 285 public void setSequence(long sequence) { 286 this.sequence = sequence; 287 } 288 289 public long getLastTimeMillis() { 290 return lastTimeMillis; 291 } 292 293 public void setLastTimeMillis(long lastTimeMillis) { 294 this.lastTimeMillis = lastTimeMillis; 295 } 296}