├── 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 | --------------------------------------------------------------------------------