1+ package io .github .gunkim .algorithm .identifier ;
2+
3+ /**
4+ * 트위터 Snowflake ID 생성기
5+ * 총 64비트 구조
6+ * - sign 1비트 : 부호 비트, 음수값으로 사용되지 않음
7+ * - timestamp 41비트 : 41비트로 표현되는 타임스탬프 (밀리초 단위)
8+ * - worker id 5비트 : 서버 ID, 최대 32(2^5)개 서버 지원
9+ * - datacenter id 5비트 : 데이터센터 ID, 최대 32(2^5)개 데이터센터 지원
10+ * - sequence 12비트 : 일련번호, 1밀리초 내에 최대 4096까지 증가하며 매 밀리초마다 초기화
11+ * <p>
12+ * Snowflake ID는 64비트 크기로 **정렬 가능**하며, **전 세계 어디에서나 독립적으로 유니크한 ID**를 생성할 수 있다.
13+ * 이론적으로, 최대 32개의 데이터센터와 각 데이터센터마다 32개의 서버를 사용할 수 있다.
14+ */
15+ public final class SnowFlakeIdentifierGenerator {
16+ private static final int DATACENTER_ID_BITS = 5 ;
17+ private static final int WORKER_ID_BITS = 5 ;
18+ private static final int SEQUENCE_BITS = 12 ;
19+
20+ private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS );
21+ private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS );
22+ private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS );
23+
24+ private final long epoch ;
25+ private final long workerId ;
26+ private final long datacenterId ;
27+
28+ private long lastTimestamp = -1L ;
29+ private long sequence = 0L ;
30+
31+ public SnowFlakeIdentifierGenerator (long epoch , long workerId , long datacenterId ) {
32+ if (workerId > MAX_WORKER_ID || workerId < 0 ) {
33+ throw new IllegalArgumentException ("Worker ID는 " + MAX_WORKER_ID + "를 초과할 수 없습니다" );
34+ }
35+ if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0 ) {
36+ throw new IllegalArgumentException ("Datacenter ID는 " + MAX_DATACENTER_ID + "를 초과할 수 없습니다" );
37+ }
38+
39+ this .epoch = epoch ;
40+ this .workerId = workerId ;
41+ this .datacenterId = datacenterId ;
42+ }
43+
44+ public synchronized SnowFlakeIdentifier nextId () {
45+ long timestamp = System .currentTimeMillis () - epoch ;
46+
47+ if (timestamp < lastTimestamp ) {
48+ throw new RuntimeException ("시스템 시계가 이전 시간으로 되돌아갔습니다" );
49+ }
50+
51+ if (lastTimestamp == timestamp ) {
52+ sequence = (sequence + 1 ) & MAX_SEQUENCE ;
53+ if (sequence == 0 ) {
54+ timestamp = tilNextMillis (lastTimestamp );
55+ }
56+ } else {
57+ sequence = 0 ;
58+ }
59+
60+ lastTimestamp = timestamp ;
61+ return new SnowFlakeIdentifier (timestamp , datacenterId , workerId , sequence );
62+ }
63+
64+ private long tilNextMillis (long lastTimestamp ) {
65+ long timestamp = System .currentTimeMillis () - epoch ;
66+ while (timestamp <= lastTimestamp ) {
67+ timestamp = System .currentTimeMillis () - epoch ;
68+ }
69+ return timestamp ;
70+ }
71+ }
0 commit comments