1
+ package com .example .springsocial .controller ;
2
+
3
+
4
+ import java .net .NetworkInterface ;
5
+ import java .security .SecureRandom ;
6
+ import java .time .Instant ;
7
+ import java .util .Enumeration ;
8
+
9
+ /**
10
+ * Distributed Sequence Generator.
11
+ * Inspired by Twitter snowflake: https://github.com/twitter/snowflake/tree/snowflake-2010
12
+ *
13
+ * This class should be used as a Singleton.
14
+ * Make sure that you create and reuse a Single instance of SequenceGenerator per machine in your distributed system cluster.
15
+ */
16
+ public class SequenceGenerator {
17
+ private static final int TOTAL_BITS = 64 ;
18
+ private static final int EPOCH_BITS = 42 ;
19
+ private static final int MACHINE_ID_BITS = 10 ;
20
+ private static final int SEQUENCE_BITS = 12 ;
21
+
22
+ private static final int maxMachineId = (int )(Math .pow (2 , MACHINE_ID_BITS ) - 1 );
23
+ private static final int maxSequence = (int )(Math .pow (2 , SEQUENCE_BITS ) - 1 );
24
+
25
+ // Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z)
26
+ private static final long CUSTOM_EPOCH = 1420070400000L ;
27
+
28
+ private final int machineId ;
29
+
30
+ private long lastTimestamp = -1L ;
31
+ private long sequence = 0L ;
32
+
33
+ // Create Snowflake with a machineId
34
+ public SequenceGenerator (int machineId ) {
35
+ if (machineId < 0 || machineId > maxMachineId ) {
36
+ throw new IllegalArgumentException (String .format ("MachineId must be between %d and %d" , 0 , maxMachineId ));
37
+ }
38
+ this .machineId = machineId ;
39
+ }
40
+
41
+ // Let Snowflake generate a machineId
42
+ public SequenceGenerator () {
43
+ this .machineId = createMachineId ();
44
+ }
45
+
46
+
47
+ public long nextId () {
48
+ long currentTimestamp = timestamp ();
49
+
50
+ synchronized (this ) {
51
+ if (currentTimestamp < lastTimestamp ) {
52
+ throw new IllegalStateException ("Invalid System Clock!" );
53
+ }
54
+
55
+ if (currentTimestamp == lastTimestamp ) {
56
+ sequence = (sequence + 1 ) & maxSequence ;
57
+ if (sequence == 0 ) {
58
+ // Sequence Exhausted, wait till next millisecond.
59
+ currentTimestamp = waitNextMillis (currentTimestamp );
60
+ }
61
+ } else {
62
+ // reset sequence for next millisecond
63
+ sequence = 0 ;
64
+ }
65
+
66
+ lastTimestamp = currentTimestamp ;
67
+ }
68
+
69
+ long id = currentTimestamp << (TOTAL_BITS - EPOCH_BITS );
70
+ id |= (machineId << (TOTAL_BITS - EPOCH_BITS - MACHINE_ID_BITS ));
71
+ id |= sequence ;
72
+ return id ;
73
+ }
74
+
75
+
76
+ // Get current timestamp in milliseconds, adjust for the custom epoch.
77
+ private static long timestamp () {
78
+ return Instant .now ().toEpochMilli () - CUSTOM_EPOCH ;
79
+ }
80
+
81
+ // Block and wait till next millisecond
82
+ private long waitNextMillis (long currentTimestamp ) {
83
+ while (currentTimestamp == lastTimestamp ) {
84
+ currentTimestamp = timestamp ();
85
+ }
86
+ return currentTimestamp ;
87
+ }
88
+
89
+ private int createMachineId () {
90
+ int machineId ;
91
+ try {
92
+ StringBuilder sb = new StringBuilder ();
93
+ Enumeration <NetworkInterface > networkInterfaces = NetworkInterface .getNetworkInterfaces ();
94
+ while (networkInterfaces .hasMoreElements ()) {
95
+ NetworkInterface networkInterface = networkInterfaces .nextElement ();
96
+ byte [] mac = networkInterface .getHardwareAddress ();
97
+ if (mac != null ) {
98
+ for (int i = 0 ; i < mac .length ; i ++) {
99
+ sb .append (String .format ("%02X" , mac [i ]));
100
+ }
101
+ }
102
+ }
103
+ machineId = sb .toString ().hashCode ();
104
+ } catch (Exception ex ) {
105
+ machineId = (new SecureRandom ().nextInt ());
106
+ }
107
+ machineId = machineId & maxMachineId ;
108
+ return machineId ;
109
+ }
110
+ }
0 commit comments