1+ package code .distribution .id .Snowflake ;
2+
3+ /**
4+ * 〈Snowflake算法〉<p>
5+ * 〈功能详细描述〉
6+ * @see https://github.com/seata/seata/blob/1.4.0/common/src/main/java/io/seata/common/util/IdWorker.java
7+ * @author tianwu
8+ * @date 2021/06/16
9+ */
10+ import java .net .InetAddress ;
11+ import java .net .UnknownHostException ;
12+ import java .util .concurrent .ThreadLocalRandom ;
13+
14+ public class IdWorker {
15+
16+ private volatile static IdWorker idWorker = null ;
17+
18+ /**
19+ * Start time cut (2020-05-03)
20+ */
21+ private final long twepoch = 1588435200000L ;
22+
23+ /**
24+ * The number of bits occupied by the machine id
25+ */
26+ private final long workerIdBits = 10L ;
27+
28+ /**
29+ * Maximum supported machine id, the result is 1023 (this shift algorithm can quickly calculate the largest decimal
30+ * number that can be represented by a few binary numbers)
31+ */
32+ private final long maxWorkerId = -1L ^ (-1L << workerIdBits );
33+
34+ /**
35+ * The number of bits the sequence occupies in id
36+ */
37+ private final long sequenceBits = 12L ;
38+
39+ /**
40+ * Machine ID left 12 digits
41+ */
42+ private final long workerIdShift = sequenceBits ;
43+
44+ /**
45+ * Time truncated to the left by 22 bits (10 + 12)
46+ */
47+ private final long timestampLeftShift = sequenceBits + workerIdBits ;
48+
49+ /**
50+ * Generate sequence mask
51+ */
52+ private final long sequenceMask = -1L ^ (-1L << sequenceBits );
53+
54+ /**
55+ * Machine ID (0 ~ 1023)
56+ */
57+ private long workerId ;
58+
59+ /**
60+ * Sequence in milliseconds (0 ~ 4095)
61+ */
62+ private long sequence = 0L ;
63+
64+ /**
65+ * Time of last ID generation
66+ */
67+ private long lastTimestamp = -1L ;
68+
69+ /**
70+ * Constructor
71+ *
72+ * @param workerId
73+ * Job ID (0 ~ 1023)
74+ */
75+ public IdWorker (long workerId ) {
76+ if (workerId > maxWorkerId || workerId < 0 ) {
77+ throw new IllegalArgumentException (
78+ String .format ("worker Id can't be greater than %d or less than 0" , maxWorkerId ));
79+ }
80+ this .workerId = workerId ;
81+ }
82+
83+ /**
84+ * Get the next ID (the method is thread-safe)
85+ *
86+ * @return SnowflakeId
87+ */
88+ public synchronized long nextId () {
89+ long timestamp = timeGen ();
90+
91+ if (timestamp < lastTimestamp ) {
92+ throw new RuntimeException (String .format (
93+ "clock moved backwards. Refusing to generate id for %d milliseconds" , lastTimestamp - timestamp ));
94+ }
95+
96+ if (lastTimestamp == timestamp ) {
97+ sequence = (sequence + 1 ) & sequenceMask ;
98+ if (sequence == 0 ) {
99+ timestamp = tilNextMillis (lastTimestamp );
100+ }
101+ } else {
102+ sequence = ThreadLocalRandom .current ().nextLong (0 , 256 );
103+ }
104+ lastTimestamp = timestamp ;
105+
106+ return ((timestamp - twepoch ) << timestampLeftShift ) | (workerId << workerIdShift ) | sequence ;
107+ }
108+
109+ /**
110+ * Block until the next millisecond until a new timestamp is obtained
111+ *
112+ * @param lastTimestamp
113+ * Time of last ID generation
114+ * @return Current timestamp
115+ */
116+ protected long tilNextMillis (long lastTimestamp ) {
117+ long timestamp = timeGen ();
118+ while (timestamp <= lastTimestamp ) {
119+ timestamp = timeGen ();
120+ }
121+ return timestamp ;
122+ }
123+
124+ /**
125+ * Returns the current time in milliseconds
126+ *
127+ * @return Current time (ms)
128+ */
129+ protected long timeGen () {
130+ return System .currentTimeMillis ();
131+ }
132+
133+ public static IdWorker getInstance () {
134+ if (idWorker == null ) {
135+ synchronized (IdWorker .class ) {
136+ if (idWorker == null ) {
137+ init (initWorkerId ());
138+ }
139+ }
140+ }
141+ return idWorker ;
142+ }
143+
144+ public static long initWorkerId () {
145+ InetAddress address ;
146+ try {
147+ address = InetAddress .getLocalHost ();
148+ } catch (final UnknownHostException e ) {
149+ throw new IllegalStateException ("Cannot get LocalHost InetAddress, please check your network!" ,e );
150+ }
151+ byte [] ipAddressByteArray = address .getAddress ();
152+ return ((ipAddressByteArray [ipAddressByteArray .length - 2 ] & 0B11) << Byte .SIZE ) + (ipAddressByteArray [ipAddressByteArray .length - 1 ] & 0xFF );
153+ }
154+
155+ public static void init (Long serverNodeId ) {
156+ if (idWorker == null ) {
157+ synchronized (IdWorker .class ) {
158+ if (idWorker == null ) {
159+ idWorker = new IdWorker (serverNodeId );
160+ }
161+ }
162+ }
163+ }
164+
165+ }
0 commit comments