├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── timestatic
│ └── snowflake
│ ├── SnowFlakeWorker.java
│ └── ZookeeperWorker.java
└── test
└── java
└── com
└── timestatic
└── snowflake
├── Sample.java
├── SnowFlakeWorkerTest.java
└── ZookeeperWorkerTest.java
/README.md:
--------------------------------------------------------------------------------
1 | # snowflake
2 | snowflake, simple and effective handle clock moved backwards.
3 |
4 | ---
5 | Example:
6 | ```java
7 | public class Sample {
8 |
9 | public static void main(String[] args) {
10 | ZookeeperWorker.init("127.0.0.1");
11 | System.out.println(SnowFlakeWorker.getInstance().nextId());
12 | System.out.println(SnowFlakeWorker.getInstance().nextId());
13 | }
14 | }
15 | ```
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.timestatic.snowflake
8 | snowflake
9 | 1.0.0-SNAPSHOT
10 |
11 |
12 | 1.8
13 | 1.8
14 | 1.8
15 |
16 |
17 |
18 |
19 |
20 | junit
21 | junit
22 | 4.12
23 | test
24 |
25 |
26 |
27 | org.powermock
28 | powermock-module-junit4
29 | 1.7.4
30 | test
31 |
32 |
33 | org.powermock
34 | powermock-api-mockito
35 | 1.7.4
36 | test
37 |
38 |
39 |
40 | com.github.sgroschupf
41 | zkclient
42 | 0.1
43 |
44 |
45 |
46 | org.slf4j
47 | slf4j-api
48 | 1.7.28
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/main/java/com/timestatic/snowflake/SnowFlakeWorker.java:
--------------------------------------------------------------------------------
1 | package com.timestatic.snowflake;
2 |
3 | /**
4 | * @Author: timestatic
5 | * @Date: 2019/11/2 22:02
6 | */
7 | public class SnowFlakeWorker {
8 |
9 | private volatile static SnowFlakeWorker snowFlakeWorkerInstance;
10 |
11 | // 1位标识部分 - 41位时间戳部分 - 10位节点部分 12位序列号部分
12 | /** 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 */
13 | /**
14 | * 起始的时间戳
15 | */
16 | private final static long START_STMP = 1288834974657L;
17 |
18 | /**
19 | * 每一部分占用的位数
20 | */
21 | private final static long SEQUENCE_BIT = 12; // 序列号占用的位数
22 | private final static long WORK_BIT = 10; // 机器标识占用的位数
23 |
24 | /**
25 | * WORK_NUM最大值 1023
26 | */
27 | private final static long MAX_WORK_NUM = -1L ^ (-1L << WORK_BIT);
28 | /**
29 | * SEQUENCE最大值 4095
30 | */
31 | private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
32 |
33 | /**
34 | * 每一部分向左的位移
35 | */
36 | private final static long WORK_LEFT = SEQUENCE_BIT;
37 | private final static long TIMESTMP_LEFT = WORK_LEFT + WORK_BIT;
38 |
39 | private long workId;
40 | private long sequence = 0L; //序列号
41 | private long lastStmp = -1L; //上一次时间戳
42 |
43 | /** 步长, 1024 */
44 | private static long stepSize = 2 << 9;
45 | /** 基础序列号, 每发生一次时钟回拨即改变, basicSequence += stepSize */
46 | private long basicSequence = 0L;
47 |
48 | private SnowFlakeWorker(long workId) {
49 | if (workId > MAX_WORK_NUM || workId < 0) {
50 | throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
51 | }
52 | this.workId = workId;
53 | }
54 |
55 |
56 | protected synchronized static SnowFlakeWorker initSnowFlakeWorker(long workId) {
57 | snowFlakeWorkerInstance = new SnowFlakeWorker(workId);
58 | return snowFlakeWorkerInstance;
59 | }
60 |
61 | public static SnowFlakeWorker getInstance() {
62 | return snowFlakeWorkerInstance;
63 | }
64 |
65 |
66 | /**
67 | * 产生下一个ID
68 | */
69 | public synchronized long nextId() {
70 | long currStmp = getNewstmp();
71 | if (currStmp < lastStmp) {
72 | return handleClockBackwards(currStmp);
73 | }
74 |
75 | if (currStmp == lastStmp) {
76 | // 相同毫秒内,序列号自增
77 | sequence = (sequence + 1) & MAX_SEQUENCE;
78 | // 同一毫秒的序列数已经达到最大
79 | if (sequence == 0L) {
80 | currStmp = getNextMill();
81 | }
82 | } else {
83 | // 不同毫秒内,序列号置为 basicSequence
84 | sequence = basicSequence;
85 | }
86 |
87 | lastStmp = currStmp;
88 |
89 | return (currStmp - START_STMP) << TIMESTMP_LEFT // 时间戳部分
90 | | workId << WORK_LEFT // 节点部分
91 | | sequence; // 序列号部分
92 | }
93 |
94 | /**
95 | * 处理时钟回拨
96 | */
97 | private long handleClockBackwards(long currStmp) {
98 | basicSequence += stepSize;
99 | if (basicSequence == MAX_SEQUENCE + 1) {
100 | basicSequence = 0;
101 | currStmp = getNextMill();
102 | }
103 | sequence = basicSequence;
104 |
105 | lastStmp = currStmp;
106 |
107 | return (currStmp - START_STMP) << TIMESTMP_LEFT // 时间戳部分
108 | | workId << WORK_LEFT // 节点部分
109 | | sequence; // 序列号部分
110 | }
111 |
112 | private long getNextMill() {
113 | long mill = getNewstmp();
114 | while (mill <= lastStmp) {
115 | mill = getNewstmp();
116 | }
117 | return mill;
118 | }
119 |
120 | private long getNewstmp() {
121 | return System.currentTimeMillis();
122 | }
123 |
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/com/timestatic/snowflake/ZookeeperWorker.java:
--------------------------------------------------------------------------------
1 | package com.timestatic.snowflake;
2 |
3 | import org.I0Itec.zkclient.IZkDataListener;
4 | import org.I0Itec.zkclient.IZkStateListener;
5 | import org.I0Itec.zkclient.ZkClient;
6 | import org.I0Itec.zkclient.exception.ZkNoNodeException;
7 | import org.apache.zookeeper.Watcher;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import java.util.List;
12 | import java.util.Set;
13 | import java.util.stream.Collectors;
14 |
15 | /**
16 | * @Author: timestatic
17 | * @Date: 2019/11/4 9:44
18 | */
19 | public final class ZookeeperWorker {
20 | private final static Logger LOGGER = LoggerFactory.getLogger(ZookeeperWorker.class);
21 |
22 | private static final Long MAX_NODE_NUM = 1024L;
23 | private static final String PARENT_FOREVER_PATH = "/leaf-snowflake";
24 | private static final String APP_PATH = "/appName";
25 | private static final String PARENT_PATH = PARENT_FOREVER_PATH + APP_PATH;
26 | /**
27 | * 临时节点
28 | */
29 | private static final String TEMP_CHILD_PATH = "/instanceId";
30 | /**
31 | * path: /leaf-snowflake/appName/instanceId
32 | */
33 | private static final String COMPLETE_PATH = PARENT_FOREVER_PATH + APP_PATH + TEMP_CHILD_PATH;
34 |
35 | private static ZkClient zkClient;
36 | private static String zkAddress;
37 |
38 | private volatile static boolean initialized = false;
39 |
40 | private static volatile Long workId;
41 |
42 | public static Long getWorkId() {
43 | return workId;
44 | }
45 |
46 | public synchronized static void init(String zookeeperAddress) {
47 | if (initialized) {
48 | return;
49 | }
50 | initialized = true;
51 |
52 | zkAddress = zookeeperAddress;
53 | createByZookeeper();
54 | addWatch(zkClient);
55 | }
56 |
57 | private static void createByZookeeper() {
58 | String address = zkAddress;
59 | zkClient = new ZkClient(address);
60 |
61 | List children;
62 | try {
63 | children = zkClient.getChildren(PARENT_PATH);
64 | if (children.isEmpty()) {
65 | zkClient.delete(PARENT_PATH);
66 | zkClient.createPersistent(PARENT_PATH, true);
67 | }
68 | } catch (ZkNoNodeException e) {
69 | zkClient.createPersistent(PARENT_PATH, true);
70 | }
71 |
72 | String data = String.valueOf(System.currentTimeMillis());
73 |
74 | // 创建临时顺序节点, 获取id
75 | String resultPath;
76 | try {
77 | resultPath = zkClient.createEphemeralSequential(COMPLETE_PATH, data);
78 | } catch (ZkNoNodeException e) {
79 | zkClient.createPersistent(PARENT_PATH, true);
80 | resultPath = zkClient.createEphemeralSequential(COMPLETE_PATH, data);
81 | }
82 |
83 | Long id = getIdFromPath(resultPath);
84 | if (id < MAX_NODE_NUM) {
85 | createWork(id);
86 | return;
87 | }
88 |
89 | zkClient.delete(resultPath);
90 |
91 | children = zkClient.getChildren(PARENT_PATH);
92 | Set ids = children.stream().map(item -> getIdFromChildPath(item))
93 | .filter(item -> item != null).collect(Collectors.toSet());
94 | for (Long i = 0L; i < MAX_NODE_NUM; i++) {
95 | if (!ids.contains(i)) {
96 | try {
97 | String path = COMPLETE_PATH + String.format("%010d", i);
98 | zkClient.createEphemeral(path, data);
99 | createWork(id);
100 | return;
101 | } catch (Exception e) {
102 | continue;
103 | }
104 | }
105 | }
106 |
107 | }
108 |
109 | private static void addWatch(ZkClient client) {
110 | // watch app节点
111 | client.subscribeDataChanges(PARENT_PATH, new IZkDataListener() {
112 | @Override
113 | public void handleDataChange(String dataPath, Object data) throws Exception {
114 | }
115 |
116 | @Override
117 | public void handleDataDeleted(String dataPath) throws Exception {
118 | LOGGER.warn("[snowflake] path={} data deleted, retry init", dataPath);
119 | createByZookeeper();
120 | }
121 | });
122 |
123 | // watch连接
124 | client.subscribeStateChanges(new IZkStateListener() {
125 | @Override
126 | public void handleStateChanged(Watcher.Event.KeeperState state) throws Exception {
127 | if (state == Watcher.Event.KeeperState.Disconnected) {
128 | LOGGER.warn("[snowflake] zkclient disconnect with server ....");
129 | }
130 | }
131 |
132 | @Override
133 | public void handleNewSession() throws Exception {
134 | LOGGER.warn("[snowflake] handleNewSession retry init");
135 | createByZookeeper();
136 | }
137 | });
138 | }
139 |
140 | private static Long getIdFromChildPath(String path) {
141 | try {
142 | String idStr = path.replace("instanceId", "");
143 | return Long.valueOf(idStr);
144 | } catch (Exception e) {
145 | return null;
146 | }
147 | }
148 |
149 | private static Long getIdFromPath(String path) {
150 | String idStr = path.replace(COMPLETE_PATH, "");
151 | return Long.valueOf(idStr);
152 | }
153 |
154 | private static void createWork(Long id) {
155 | workId = id;
156 | SnowFlakeWorker.initSnowFlakeWorker(id);
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/src/test/java/com/timestatic/snowflake/Sample.java:
--------------------------------------------------------------------------------
1 | package com.timestatic.snowflake;
2 |
3 | /**
4 | * @Author: timestatic
5 | * @Date: 2019/11/5 20:39
6 | */
7 | public class Sample {
8 |
9 | public static void main(String[] args) {
10 | ZookeeperWorker.init("127.0.0.1");
11 | System.out.println(SnowFlakeWorker.getInstance().nextId());
12 | System.out.println(SnowFlakeWorker.getInstance().nextId());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/test/java/com/timestatic/snowflake/SnowFlakeWorkerTest.java:
--------------------------------------------------------------------------------
1 | package com.timestatic.snowflake;
2 |
3 |
4 | import org.junit.Assert;
5 | import org.junit.Before;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.powermock.api.mockito.PowerMockito;
9 | import org.powermock.core.classloader.annotations.PrepareForTest;
10 | import org.powermock.modules.junit4.PowerMockRunner;
11 |
12 | import java.util.HashSet;
13 | import java.util.Set;
14 | import java.util.concurrent.ConcurrentHashMap;
15 | import java.util.concurrent.CountDownLatch;
16 | import java.util.concurrent.Executor;
17 | import java.util.concurrent.Executors;
18 | import java.util.concurrent.TimeUnit;
19 |
20 | import static org.hamcrest.core.Is.is;
21 |
22 | /**
23 | * @Author: timestatic
24 | * @Date: 2019/11/2 22:32
25 | */
26 | @RunWith(PowerMockRunner.class)
27 | @PrepareForTest(value = {System.class, SnowFlakeWorker.class})
28 | public class SnowFlakeWorkerTest {
29 |
30 | private SnowFlakeWorker snowFlakeWorker;
31 | private static long workId = 1L;
32 |
33 | @Before
34 | public void init() {
35 | SnowFlakeWorker.initSnowFlakeWorker(workId);
36 | snowFlakeWorker = SnowFlakeWorker.getInstance();
37 | }
38 |
39 | @Test
40 | public void initErrorTest() {
41 | try {
42 | SnowFlakeWorker.initSnowFlakeWorker(1024L);
43 | } catch (Exception e) {
44 | Assert.assertThat(e.getMessage(), is("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"));
45 | }
46 | try {
47 | SnowFlakeWorker.initSnowFlakeWorker(-3L);
48 | } catch (Exception e) {
49 | Assert.assertThat(e.getMessage(), is("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"));
50 | }
51 |
52 | }
53 |
54 | @Test
55 | public void testCreateId() throws InterruptedException {
56 | int count = 10;
57 | ConcurrentHashMap resultSet = new ConcurrentHashMap<>();
58 | Executor executor = Executors.newFixedThreadPool(count);
59 | CountDownLatch countDownLatch = new CountDownLatch(count);
60 | for (int i = 0; i < count; i++) {
61 | executor.execute(() -> {
62 | resultSet.put(snowFlakeWorker.nextId(), 0);
63 | countDownLatch.countDown();
64 | });
65 | }
66 | countDownLatch.await();
67 | Assert.assertEquals(count, resultSet.size());
68 | }
69 |
70 |
71 | private final static long START_STMP = 1288834974657L;
72 | private final static long SEQUENCE_BIT = 12;
73 | private final static long WORK_BIT = 10;
74 | private final static long WORK_LEFT = SEQUENCE_BIT;
75 | private final static long TIMESTMP_LEFT = WORK_LEFT + WORK_BIT;
76 | private final static long STEP_SIZE = 1024L;
77 |
78 | @Test
79 | public void testClockBackwards() throws InterruptedException {
80 | int size = 100;
81 | long beginTime = System.currentTimeMillis();
82 | Set resultSet = new HashSet<>(128);
83 | for (int i = 0; i < size; i++) {
84 | resultSet.add(snowFlakeWorker.nextId());
85 | }
86 | TimeUnit.SECONDS.sleep(1);
87 | PowerMockito.mockStatic(System.class);
88 | // 第一次回拨
89 | PowerMockito.when(System.currentTimeMillis()).thenReturn(beginTime);
90 |
91 | Assert.assertEquals(beginTime, System.currentTimeMillis());
92 |
93 | for (int i = 0; i < 5; i++) {
94 | // i > 0, currStmp == lastStmp
95 | long temp = (beginTime - START_STMP) << TIMESTMP_LEFT
96 | | workId << WORK_LEFT
97 | | (STEP_SIZE + i);
98 | long tmpR;
99 | resultSet.add(tmpR = snowFlakeWorker.nextId());
100 | Assert.assertEquals(temp, tmpR);
101 | }
102 | Assert.assertEquals(size + 5, resultSet.size());
103 |
104 | // 下一毫秒
105 | PowerMockito.when(System.currentTimeMillis()).thenReturn(beginTime += 1);
106 | long temp = (beginTime - START_STMP) << TIMESTMP_LEFT | workId << WORK_LEFT | STEP_SIZE;
107 | Assert.assertEquals(temp, snowFlakeWorker.nextId());
108 | // 同一毫秒内
109 | temp = (beginTime - START_STMP) << TIMESTMP_LEFT | workId << WORK_LEFT | (STEP_SIZE + 1);
110 | Assert.assertEquals(temp, snowFlakeWorker.nextId());
111 | temp = (beginTime - START_STMP) << TIMESTMP_LEFT | workId << WORK_LEFT | (STEP_SIZE + 2);
112 | Assert.assertEquals(temp, snowFlakeWorker.nextId());
113 |
114 | // 第二次回拨
115 | PowerMockito.when(System.currentTimeMillis()).thenReturn(beginTime -= 5);
116 | temp = (beginTime - START_STMP) << TIMESTMP_LEFT | workId << WORK_LEFT | (STEP_SIZE * 2);
117 | Assert.assertEquals(temp, snowFlakeWorker.nextId());
118 | // 不同毫秒内
119 | PowerMockito.when(System.currentTimeMillis()).thenReturn(beginTime += 1);
120 | temp = (beginTime - START_STMP) << TIMESTMP_LEFT | workId << WORK_LEFT | (STEP_SIZE * 2);
121 | Assert.assertEquals(temp, snowFlakeWorker.nextId());
122 |
123 | // 第三次回拨
124 | PowerMockito.when(System.currentTimeMillis()).thenReturn(beginTime -= 10);
125 | temp = (beginTime - START_STMP) << TIMESTMP_LEFT | workId << WORK_LEFT | (STEP_SIZE * 3);
126 | Assert.assertEquals(temp, snowFlakeWorker.nextId());
127 | // 同一毫秒内
128 | temp = (beginTime - START_STMP) << TIMESTMP_LEFT | workId << WORK_LEFT | (STEP_SIZE * 3 + 1);
129 | Assert.assertEquals(temp, snowFlakeWorker.nextId());
130 | temp = (beginTime - START_STMP) << TIMESTMP_LEFT | workId << WORK_LEFT | (STEP_SIZE * 3 + 2);
131 | Assert.assertEquals(temp, snowFlakeWorker.nextId());
132 | }
133 |
134 | }
--------------------------------------------------------------------------------
/src/test/java/com/timestatic/snowflake/ZookeeperWorkerTest.java:
--------------------------------------------------------------------------------
1 | package com.timestatic.snowflake;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | /**
8 | * @FileName: ZookeeperWorkerTest.java
9 | * @Description: ZookeeperWorkerTest.java类说明
10 | * @Author: jiangqian
11 | * @Date: 2019/11/5 19:54
12 | */
13 | public class ZookeeperWorkerTest {
14 |
15 | @Before
16 | public void init() {
17 | ZookeeperWorker.init("127.0.0.1:2181");
18 | }
19 |
20 | @Test
21 | public void test() {
22 | Assert.assertNotNull(ZookeeperWorker.getWorkId());
23 | Assert.assertEquals(ZookeeperWorker.getWorkId(), ZookeeperWorker.getWorkId());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------