¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.aps.common.core.utils.uuid; |
| | | |
| | | |
| | | import com.aps.common.core.enums.SystemClock; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.lang.management.ManagementFactory; |
| | | import java.net.InetAddress; |
| | | import java.net.NetworkInterface; |
| | | import java.util.Enumeration; |
| | | import java.util.concurrent.ThreadLocalRandom; |
| | | import java.util.regex.Pattern; |
| | | |
| | | /** |
| | | * åºäºTwitterçSnowflakeç®æ³å®ç°åå¸å¼é«ææåºIDç产é»ç§æ(sequence)ââå级çSnowflake |
| | | * |
| | | * <br> |
| | | * SnowFlakeçç»æå¦ä¸(æ¯é¨åç¨-åå¼):<br> |
| | | * <br> |
| | | * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br> |
| | | * <br> |
| | | * 1使 è¯ï¼ç±äºlongåºæ¬ç±»åå¨Java䏿¯å¸¦ç¬¦å·çï¼æé«ä½æ¯ç¬¦å·ä½ï¼æ£æ°æ¯0ï¼è´æ°æ¯1ï¼æä»¥idä¸è¬æ¯æ£æ°ï¼æé«ä½æ¯0<br> |
| | | * <br> |
| | | * 41使¶é´æª(毫ç§çº§)ï¼æ³¨æï¼41使¶é´æªä¸æ¯åå¨å½åæ¶é´çæ¶é´æªï¼èæ¯å卿¶é´æªçå·®å¼ï¼å½åæ¶é´æª - å¼å§æ¶é´æª) |
| | | * å¾å°çå¼ï¼ï¼è¿éççå¼å§æ¶é´æªï¼ä¸è¬æ¯æä»¬çidçæå¨å¼å§ä½¿ç¨çæ¶é´ï¼ç±æä»¬ç¨åºæ¥æå®çï¼å¦ä¸START_TIME屿§ï¼ã41ä½çæ¶é´æªï¼å¯ä»¥ä½¿ç¨69å¹´ï¼å¹´T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br> |
| | | * <br> |
| | | * 10ä½çæ°æ®æºå¨ä½ï¼å¯ä»¥é¨ç½²å¨1024个èç¹ï¼å
æ¬5ä½dataCenterIdå5ä½workerId<br> |
| | | * <br> |
| | | * 12ä½åºåï¼æ¯«ç§å
ç计æ°ï¼12ä½ç计æ°é¡ºåºå·æ¯ææ¯ä¸ªèç¹æ¯æ¯«ç§(å䏿ºå¨ï¼å䏿¶é´æª)产ç4096个IDåºå·<br> |
| | | * <br> |
| | | * <br> |
| | | * å èµ·æ¥å好64ä½ï¼ä¸ºä¸ä¸ªLongåã<br> |
| | | * SnowFlakeçä¼ç¹æ¯ï¼æ´ä½ä¸æç
§æ¶é´èªå¢æåºï¼å¹¶ä¸æ´ä¸ªåå¸å¼ç³»ç»å
ä¸ä¼äº§çID碰æ(ç±æ°æ®ä¸å¿IDåæºå¨IDä½åºå)ï¼å¹¶ä¸æçè¾é«ï¼ç»æµè¯ï¼SnowFlakeæ¯ç§è½å¤äº§ç26ä¸IDå·¦å³ã |
| | | * <p> |
| | | * <p> |
| | | * ç¹æ§ï¼ |
| | | * 1.æ¯æèªå®ä¹å
许æ¶é´åæ¨çèå´<p> |
| | | * 2.è§£å³è·¨æ¯«ç§èµ·å§å¼æ¯æ¬¡ä¸º0å¼å§çæ
åµï¼é¿å
æ«å°¾å¿
å®ä¸ºå¶æ°ï¼èä¸ä¾¿äºåä½ä½¿ç¨é®é¢ï¼<p> |
| | | * 3.è§£å³é«å¹¶ååºæ¯ä¸è·åæ¶é´æ³æ§è½é®é¢<p> |
| | | * 4.æ¯ææ ¹æ®IPæ«å°¾æ°æ®ä½ä¸ºworkerId |
| | | * 5.æ¶é´åæ¨æ¹æ¡æèï¼1024个èç¹ä¸åé
10个ç¹ä½ä¸ºæ¶é´åæ¨åºå·ï¼è¿ç»10次æ¶é´åæ¨çæ¦çè¾å°ï¼ |
| | | * <p> |
| | | * 常è§é®é¢: |
| | | * 1.æ¶é´åæ¨é®é¢ |
| | | * 2.æºå¨idçåé
ååæ¶é®é¢ |
| | | * 3.æºå¨idçä¸éé®é¢ |
| | | * |
| | | * @author lry |
| | | * @version 3.0 |
| | | */ |
| | | @Component |
| | | public class Sequence { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(Sequence.class); |
| | | |
| | | /** |
| | | * æ¶é´èµ·å§æ è®°ç¹ï¼ä½ä¸ºåºåï¼ä¸è¬åç³»ç»çæè¿æ¶é´ï¼ä¸æ¦ç¡®å®ä¸è½åå¨ï¼ |
| | | */ |
| | | private final long twepoch = 1519740777809L; |
| | | |
| | | /** |
| | | * 5ä½çæºæ¿id |
| | | */ |
| | | private final long datacenterIdBits = 5L; |
| | | /** |
| | | * 5ä½çæºå¨id |
| | | */ |
| | | private final long workerIdBits = 5L; |
| | | /** |
| | | * æ¯æ¯«ç§å
产ççidæ°: 2ç12次æ¹ä¸ª |
| | | */ |
| | | private final long sequenceBits = 12L; |
| | | |
| | | protected final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); |
| | | protected final long maxWorkerId = -1L ^ (-1L << workerIdBits); |
| | | |
| | | 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); |
| | | |
| | | /** |
| | | * æå±æºæ¿id |
| | | */ |
| | | private final long datacenterId; |
| | | /** |
| | | * æå±æºå¨id |
| | | */ |
| | | private final long workerId; |
| | | /** |
| | | * å¹¶åæ§å¶åºå |
| | | */ |
| | | private long sequence = 0L; |
| | | |
| | | /** |
| | | * 䏿¬¡ç产 ID æ¶é´æ³ |
| | | */ |
| | | private long lastTimestamp = -1L; |
| | | |
| | | private static volatile InetAddress LOCAL_ADDRESS = null; |
| | | private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$"); |
| | | |
| | | public Sequence() { |
| | | this.datacenterId = getDatacenterId(); |
| | | this.workerId = getMaxWorkerId(datacenterId); |
| | | } |
| | | |
| | | /** |
| | | * æåæé å¨ |
| | | * |
| | | * @param workerId 工使ºå¨ ID |
| | | * @param datacenterId åºåå· |
| | | */ |
| | | public Sequence(long workerId, long datacenterId) { |
| | | if (workerId > maxWorkerId || workerId < 0) { |
| | | throw new IllegalArgumentException(String.format("Worker Id can't be greater than %d or less than 0", maxWorkerId)); |
| | | } |
| | | if (datacenterId > maxDatacenterId || datacenterId < 0) { |
| | | throw new IllegalArgumentException(String.format("Datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); |
| | | } |
| | | |
| | | this.workerId = workerId; |
| | | this.datacenterId = datacenterId; |
| | | } |
| | | |
| | | /** |
| | | * åºäºç½å¡MACå°å计ç®ä½æ°ä½ä¸ºæ°æ®ä¸å¿ |
| | | * <p> |
| | | * å¯èªå®æ©å± |
| | | */ |
| | | protected long getDatacenterId() { |
| | | long id = 0L; |
| | | try { |
| | | NetworkInterface network = NetworkInterface.getByInetAddress(getLocalAddress()); |
| | | if (null == network) { |
| | | id = 1L; |
| | | } else { |
| | | byte[] mac = network.getHardwareAddress(); |
| | | if (null != mac) { |
| | | id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6; |
| | | id = id % (maxDatacenterId + 1); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn(" getDatacenterId: " + e.getMessage()); |
| | | } |
| | | |
| | | return id; |
| | | } |
| | | |
| | | /** |
| | | * åºäº MAC + PID ç hashcode è·å16个ä½ä½ |
| | | * <p> |
| | | * å¯èªå®æ©å± |
| | | */ |
| | | protected long getMaxWorkerId(long datacenterId) { |
| | | StringBuilder mpId = new StringBuilder(); |
| | | mpId.append(datacenterId); |
| | | String name = ManagementFactory.getRuntimeMXBean().getName(); |
| | | if (name != null && name.length() > 0) { |
| | | // GET jvmPid |
| | | mpId.append(name.split("@")[0]); |
| | | } |
| | | |
| | | // MAC + PID ç hashcode è·å16个ä½ä½ |
| | | return (mpId.toString().hashCode() & 0xffff) % (maxWorkerId + 1); |
| | | } |
| | | |
| | | /** |
| | | * è·åä¸ä¸ä¸ª ID |
| | | * |
| | | * @return next id |
| | | */ |
| | | public synchronized long nextId() { |
| | | long timestamp = timeGen(); |
| | | // é°ç§ |
| | | if (timestamp < lastTimestamp) { |
| | | long offset = lastTimestamp - timestamp; |
| | | if (offset <= 5) { |
| | | 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) { |
| | | // å䏿¯«ç§çåºåæ°å·²ç»è¾¾å°æå¤§ |
| | | timestamp = tilNextMillis(lastTimestamp); |
| | | } |
| | | } else { |
| | | // ä¸å毫ç§å
ï¼åºåå·ç½®ä¸º 1 - 3 éæºæ° |
| | | sequence = ThreadLocalRandom.current().nextLong(1, 3); |
| | | } |
| | | |
| | | 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.INSTANCE.currentTimeMillis(); |
| | | } |
| | | |
| | | /** |
| | | * Find first valid IP from local network card |
| | | * |
| | | * @return first valid local IP |
| | | */ |
| | | public static InetAddress getLocalAddress() { |
| | | if (LOCAL_ADDRESS != null) { |
| | | return LOCAL_ADDRESS; |
| | | } |
| | | |
| | | LOCAL_ADDRESS = getLocalAddress0(); |
| | | return LOCAL_ADDRESS; |
| | | } |
| | | |
| | | private static InetAddress getLocalAddress0() { |
| | | InetAddress localAddress = null; |
| | | try { |
| | | localAddress = InetAddress.getLocalHost(); |
| | | if (isValidAddress(localAddress)) { |
| | | return localAddress; |
| | | } |
| | | } catch (Throwable e) { |
| | | log.warn("Failed to retrieving ip address, " + e.getMessage(), e); |
| | | } |
| | | |
| | | try { |
| | | Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); |
| | | if (interfaces != null) { |
| | | while (interfaces.hasMoreElements()) { |
| | | try { |
| | | NetworkInterface network = interfaces.nextElement(); |
| | | Enumeration<InetAddress> addresses = network.getInetAddresses(); |
| | | while (addresses.hasMoreElements()) { |
| | | try { |
| | | InetAddress address = addresses.nextElement(); |
| | | if (isValidAddress(address)) { |
| | | return address; |
| | | } |
| | | } catch (Throwable e) { |
| | | log.warn("Failed to retrieving ip address, " + e.getMessage(), e); |
| | | } |
| | | } |
| | | } catch (Throwable e) { |
| | | log.warn("Failed to retrieving ip address, " + e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | } catch (Throwable e) { |
| | | log.warn("Failed to retrieving ip address, " + e.getMessage(), e); |
| | | } |
| | | |
| | | log.error("Could not get local host ip address, will use 127.0.0.1 instead."); |
| | | return localAddress; |
| | | } |
| | | |
| | | private static boolean isValidAddress(InetAddress address) { |
| | | if (address == null || address.isLoopbackAddress()) { |
| | | return false; |
| | | } |
| | | |
| | | String name = address.getHostAddress(); |
| | | return (name != null && !"0.0.0.0".equals(name) && !"127.0.0.1".equals(name) && IP_PATTERN.matcher(name).matches()); |
| | | } |
| | | |
| | | } |