1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
| @Slf4j public class IdGeneratorTest {
@Test public void test() { int initialCapacity = 1000; Set<Long> ids = new HashSet<>(initialCapacity); IdGenerator idGenerator = new IdGenerator(3, 5); for (int i = 0; i < initialCapacity; i++) { long id = idGenerator.nextId(); ids.add(id); IdGenerator.log(id); } assertEquals(ids.size(), initialCapacity); }
}
@Slf4j class IdGenerator {
private long timestamp; private int dataCenterNo; private int workerNo; private int seqNo; private int ext;
private static final int DATA_CENTER_NO_BITS = 2; private static final int WORKER_NO_BITS = 5; private static final int SEQ_NO_BITS = 12; private static final int EXT_BITS = 3;
private static final int TIMESTAMP_SHIFT = DATA_CENTER_NO_BITS + WORKER_NO_BITS + SEQ_NO_BITS + EXT_BITS; private static final int DATA_CENTER_NO_SHIFT = SEQ_NO_BITS + WORKER_NO_BITS + EXT_BITS; private static final int WORKER_NO_SHIFT = SEQ_NO_BITS + EXT_BITS; private static final int SEQ_NO_SHIFT = EXT_BITS;
private static final int MAX_DATA_CENTER_NO = (1 << DATA_CENTER_NO_BITS) - 0B1; private static final int MAX_WORKER_NO = (1 << WORKER_NO_BITS) - 0B1; private static final int MAX_SEQ_NO = (1 << SEQ_NO_BITS) - 0B1; private static final int MAX_EXT = (1 << EXT_BITS) - 0B1; private static final int MAX_BACKWARD_MILLIS = 5;
public IdGenerator(int dataCenterNo, int workerNo) { if (Integer.toBinaryString(dataCenterNo).length() > DATA_CENTER_NO_BITS) { throw new IllegalArgumentException(String.format("当前数据中心编号 %s 超限,最大支持 %s", dataCenterNo, MAX_DATA_CENTER_NO)); } else if (Integer.toBinaryString(workerNo).length() > WORKER_NO_BITS) { throw new IllegalArgumentException(String.format("当前机器编号 %s 超限,最大支持 %s", workerNo, MAX_WORKER_NO)); } this.dataCenterNo = dataCenterNo; this.workerNo = workerNo; }
public synchronized long nextId() { long now = System.currentTimeMillis(); if (timestamp == 0 || timestamp < now) { timestamp = now; seqNo = 0; } else if (timestamp == now) { if (seqNo < MAX_SEQ_NO) { seqNo++; } else { log.warn("序列号已耗尽,等待重新生成。seqNo = {}, MAX_SEQ_NO = {}", seqNo, MAX_SEQ_NO); return sleepAndNextId(1); } } else { return nextIdForBackward(now); }
return timestamp << TIMESTAMP_SHIFT | dataCenterNo << DATA_CENTER_NO_SHIFT | workerNo << WORKER_NO_SHIFT | seqNo << SEQ_NO_SHIFT | ext; }
private long nextIdForBackward(long now) { log.warn("发生时间回拨,timestamp = {}, now = {}", timestamp, now);
long duration = timestamp - now; if (duration <= MAX_BACKWARD_MILLIS) { return sleepAndNextId(duration); } else { if (ext < MAX_EXT) { ext++; timestamp = now; return nextId(); } else { throw new IllegalStateException(String.format("扩展位已耗尽。ext = %s, MAX_EXT = %s", ext, MAX_EXT)); } } }
@SneakyThrows private long sleepAndNextId(long millis) { Thread.sleep(millis); return nextId(); }
public static void log(long id) { long timestamp = id >> TIMESTAMP_SHIFT; long dataCenterNo = id >> DATA_CENTER_NO_SHIFT & MAX_DATA_CENTER_NO; long workerNo = id >> WORKER_NO_SHIFT & MAX_WORKER_NO; long seqNo = id >> SEQ_NO_SHIFT & MAX_SEQ_NO; long ext = id & MAX_EXT;
log.info("Binary is {}, id is {}", Long.toBinaryString(id), id); log.info("Binary is {}, time is {}", Long.toBinaryString(timestamp), getLocalDateTime(timestamp)); log.info("Binary is {}, dataCenterNo is {}", Long.toBinaryString(dataCenterNo), dataCenterNo); log.info("Binary is {}, workerNo is {}", Long.toBinaryString(workerNo), workerNo); log.info("Binary is {}, seqNo is {}", Long.toBinaryString(seqNo), seqNo); log.info("Binary is {}, ext is {}", Long.toBinaryString(ext), ext); }
private static LocalDateTime getLocalDateTime(long timestamp) { return Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDateTime(); }
}
|