├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── sohu │ └── idcenter │ ├── Base62.java │ ├── IdWorker.java │ └── SidWorker.java └── test └── java ├── com └── sohu │ └── idcenter │ └── Base62Test.java └── demo ├── IdWorkderDemo.java └── SidWorkerDemo.java /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | *.tmp 5 | target 6 | *.log 7 | *.tgz 8 | /data 9 | /build 10 | /docs 11 | /doc 12 | __pycache__ 13 | *.pyc 14 | *.swp 15 | /.idea 16 | *.iml 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | idcenter 2 | ==== 3 | A network service for generating unique ID numbers. 4 | 5 | (Algorithm from twitter [snowflake](https://github.com/twitter/snowflake)) 6 | 7 | This package includes a base62 tools to reduce the number of digits. 8 | 9 | usage 10 | ==== 11 | 12 | * mvnrepository [http://mvnrepository.com/artifact/com.sohu/idcenter/](http://mvnrepository.com/artifact/com.sohu/idcenter/) 13 | * centra.maven.org [http://central.maven.org/maven2/com/sohu/idcenter/](http://central.maven.org/maven2/com/sohu/idcenter/) 14 | 15 | maven: 16 | 17 | 18 | com.sohu 19 | idcenter 20 | 1.1.1 21 | 22 | 23 | gradle: 24 | 25 | 'com.sohu:idcenter:1.1.1' -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.sohu 4 | idcenter 5 | 2.2.2 6 | idcenter 7 | https://github.com/adyliu/idcenter 8 | 9 | 10 | imxylz 11 | Ady Liu 12 | imxylz@gmail.com 13 | https://github.com/adyliu 14 | 15 | developer 16 | 17 | 18 | 19 | 20 | scm:git:git@github.com:adyliu/idcenter.git 21 | scm:git:git@github.com:adyliu/idcenter.git 22 | https://github.com/adyliu/idcenter 23 | 24 | 25 | 26 | The Apache Software License, Version 2.0 27 | http://www.apache.org/licenses/LICENSE-2.0.txt 28 | repo 29 | 30 | 31 | 32 | 33 | junit 34 | junit 35 | 4.10 36 | test 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 3.1 45 | 46 | 1.6 47 | 1.6 48 | UTF-8 49 | 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-resources-plugin 54 | 2.6 55 | 56 | UTF-8 57 | 58 | 59 | 60 | 61 | org.sonatype.plugins 62 | nexus-staging-maven-plugin 63 | 1.6.3 64 | true 65 | 66 | ossrh 67 | https://oss.sonatype.org/ 68 | true 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-gpg-plugin 74 | 1.5 75 | 76 | true 77 | 78 | 79 | 80 | sign-artifacts 81 | 82 | sign 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | ossrh 92 | https://oss.sonatype.org/content/repositories/snapshots 93 | 94 | 95 | ossrh 96 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/main/java/com/sohu/idcenter/Base62.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.sohu.idcenter; 19 | 20 | /** 21 | * A Base62 method 22 | * @author adyliu (imxylz@gmail.com) 23 | * @since 1.0 24 | */ 25 | public class Base62 { 26 | 27 | private static final String baseDigits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 28 | private static final int BASE = baseDigits.length(); 29 | private static final char[] digitsChar = baseDigits.toCharArray(); 30 | private static final int FAST_SIZE = 'z'; 31 | private static final int[] digitsIndex = new int[FAST_SIZE + 1]; 32 | 33 | 34 | static { 35 | for (int i = 0; i < FAST_SIZE; i++) { 36 | digitsIndex[i] = -1; 37 | } 38 | // 39 | for (int i = 0; i < BASE; i++) { 40 | digitsIndex[digitsChar[i]] = i; 41 | } 42 | } 43 | 44 | public static long decode(String s) { 45 | long result = 0L; 46 | long multiplier = 1; 47 | for (int pos = s.length() - 1; pos >= 0; pos--) { 48 | int index = getIndex(s, pos); 49 | result += index * multiplier; 50 | multiplier *= BASE; 51 | } 52 | return result; 53 | } 54 | 55 | public static String encode(long number) { 56 | if (number < 0) throw new IllegalArgumentException("Number(Base62) must be positive: " + number); 57 | if (number == 0) return "0"; 58 | StringBuilder buf = new StringBuilder(); 59 | while (number != 0) { 60 | buf.append(digitsChar[(int) (number % BASE)]); 61 | number /= BASE; 62 | } 63 | return buf.reverse().toString(); 64 | } 65 | 66 | private static int getIndex(String s, int pos) { 67 | char c = s.charAt(pos); 68 | if (c > FAST_SIZE) { 69 | throw new IllegalArgumentException("Unknow character for Base62: " + s); 70 | } 71 | int index = digitsIndex[c]; 72 | if (index == -1) { 73 | throw new IllegalArgumentException("Unknow character for Base62: " + s); 74 | } 75 | return index; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/sohu/idcenter/IdWorker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.sohu.idcenter; 19 | 20 | 21 | import java.util.Random; 22 | 23 | /** 24 | * from https://github.com/twitter/snowflake/blob/master/src/main/scala/com/twitter/service/snowflake/IdWorker.scala 25 | * 26 | * @author adyliu (imxylz@gmail.com) 27 | * @since 1.0 28 | */ 29 | public class IdWorker { 30 | 31 | private final long workerId; 32 | private final long datacenterId; 33 | private final long idepoch; 34 | 35 | private static final long workerIdBits = 5L; 36 | private static final long datacenterIdBits = 5L; 37 | private static final long maxWorkerId = -1L ^ (-1L << workerIdBits); 38 | private static final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); 39 | 40 | private static final long sequenceBits = 12L; 41 | private static final long workerIdShift = sequenceBits; 42 | private static final long datacenterIdShift = sequenceBits + workerIdBits; 43 | private static final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; 44 | private static final long sequenceMask = -1L ^ (-1L << sequenceBits); 45 | 46 | private long lastTimestamp = -1L; 47 | private long sequence; 48 | private static final Random r = new Random(); 49 | 50 | public IdWorker() { 51 | this(1344322705519L); 52 | } 53 | 54 | public IdWorker(long idepoch) { 55 | this(r.nextInt((int) maxWorkerId), r.nextInt((int) maxDatacenterId), 0, idepoch); 56 | } 57 | 58 | public IdWorker(long workerId, long datacenterId, long sequence) { 59 | this(workerId, datacenterId, sequence, 1344322705519L); 60 | } 61 | 62 | // 63 | public IdWorker(long workerId, long datacenterId, long sequence, long idepoch) { 64 | this.workerId = workerId; 65 | this.datacenterId = datacenterId; 66 | this.sequence = sequence; 67 | this.idepoch = idepoch; 68 | if (workerId < 0 || workerId > maxWorkerId) { 69 | throw new IllegalArgumentException("workerId is illegal: " + workerId); 70 | } 71 | if (datacenterId < 0 || datacenterId > maxDatacenterId) { 72 | throw new IllegalArgumentException("datacenterId is illegal: " + workerId); 73 | } 74 | if (idepoch >= System.currentTimeMillis()) { 75 | throw new IllegalArgumentException("idepoch is illegal: " + idepoch); 76 | } 77 | } 78 | 79 | public long getDatacenterId() { 80 | return datacenterId; 81 | } 82 | 83 | public long getWorkerId() { 84 | return workerId; 85 | } 86 | 87 | public long getTime() { 88 | return System.currentTimeMillis(); 89 | } 90 | 91 | public long getId() { 92 | long id = nextId(); 93 | return id; 94 | } 95 | 96 | private synchronized long nextId() { 97 | long timestamp = timeGen(); 98 | if (timestamp < lastTimestamp) { 99 | throw new IllegalStateException("Clock moved backwards."); 100 | } 101 | if (lastTimestamp == timestamp) { 102 | sequence = (sequence + 1) & sequenceMask; 103 | if (sequence == 0) { 104 | timestamp = tilNextMillis(lastTimestamp); 105 | } 106 | } else { 107 | sequence = 0; 108 | } 109 | lastTimestamp = timestamp; 110 | long id = ((timestamp - idepoch) << timestampLeftShift)// 111 | | (datacenterId << datacenterIdShift)// 112 | | (workerId << workerIdShift)// 113 | | sequence; 114 | return id; 115 | } 116 | 117 | /** 118 | * get the timestamp (millis second) of id 119 | * @param id the nextId 120 | * @return the timestamp of id 121 | */ 122 | public long getIdTimestamp(long id){ 123 | return idepoch + (id >> timestampLeftShift); 124 | } 125 | 126 | private long tilNextMillis(long lastTimestamp) { 127 | long timestamp = timeGen(); 128 | while (timestamp <= lastTimestamp) { 129 | timestamp = timeGen(); 130 | } 131 | return timestamp; 132 | } 133 | 134 | private long timeGen() { 135 | return System.currentTimeMillis(); 136 | } 137 | 138 | @Override 139 | public String toString() { 140 | final StringBuilder sb = new StringBuilder("IdWorker{"); 141 | sb.append("workerId=").append(workerId); 142 | sb.append(", datacenterId=").append(datacenterId); 143 | sb.append(", idepoch=").append(idepoch); 144 | sb.append(", lastTimestamp=").append(lastTimestamp); 145 | sb.append(", sequence=").append(sequence); 146 | sb.append('}'); 147 | return sb.toString(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/com/sohu/idcenter/SidWorker.java: -------------------------------------------------------------------------------- 1 | package com.sohu.idcenter; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | /** 7 | * generator of 19 bits number with timestamp 8 | * 9 | * @author adyliu (imxylz@gmail.com) 10 | * @since 2016-06-28 11 | */ 12 | public class SidWorker { 13 | 14 | private static long lastTimestamp = -1L; 15 | private static int sequence = 0; 16 | private static final long MAX_SEQUENCE = 100; 17 | private static final SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS"); 18 | 19 | /** 20 | * 19 bits number with timestamp (20160628175532000002) 21 | * 22 | * @return 19 bits number with timestamp 23 | */ 24 | public static synchronized long nextSid() { 25 | long now = timeGen(); 26 | if (now == lastTimestamp) { 27 | if (sequence++ > MAX_SEQUENCE) { 28 | now = tilNextMillis(lastTimestamp); 29 | sequence = 0; 30 | } 31 | } else { 32 | sequence = 0; 33 | } 34 | lastTimestamp = now; 35 | // 36 | return 100L * Long.parseLong(format.format(new Date(now))) + sequence; 37 | } 38 | 39 | private static long tilNextMillis(long lastTimestamp) { 40 | long timestamp = timeGen(); 41 | while (timestamp <= lastTimestamp) { 42 | timestamp = timeGen(); 43 | } 44 | return timestamp; 45 | } 46 | 47 | private static long timeGen() { 48 | return System.currentTimeMillis(); 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/com/sohu/idcenter/Base62Test.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.sohu.idcenter; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import org.junit.Test; 23 | 24 | 25 | /** 26 | * @author adyliu (imxylz@gmail.com) 27 | * @since 2012-8-7 28 | */ 29 | public class Base62Test { 30 | 31 | /** 32 | * Test method for {@link com.sohu.idcenter.Base62#encode(long)}. 33 | */ 34 | @Test 35 | public void testEncode() { 36 | assertEquals("1IwymnQs", Base62.encode(6050648952832L)); 37 | } 38 | 39 | /** 40 | * Test method for {@link com.sohu.idcenter.Base62#decode(java.lang.String)}. 41 | */ 42 | @Test 43 | public void testDecode() { 44 | assertEquals(6050648952832L, Base62.decode("1IwymnQs")); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/demo/IdWorkderDemo.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.sohu.idcenter.IdWorker; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | 9 | /** 10 | * @author adyliu (imxylz@gmail.com) 11 | * @since 2015-01-19 12 | */ 13 | public class IdWorkderDemo { 14 | 15 | public static void main(String[] args) { 16 | 17 | final long idepo = System.currentTimeMillis() - 3600 * 1000L; 18 | IdWorker iw = new IdWorker(1, 1, 0, idepo); 19 | IdWorker iw2 = new IdWorker(idepo); 20 | for (int i = 0; i < 10; i++) { 21 | System.out.println(iw.getId() + " -> " + iw2.getId()); 22 | } 23 | System.out.println(iw); 24 | System.out.println(iw2); 25 | long nextId = iw.getId(); 26 | System.out.println(nextId); 27 | long time = iw.getIdTimestamp(nextId); 28 | System.out.println(time+" -> "+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(time))); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/demo/SidWorkerDemo.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import com.sohu.idcenter.SidWorker; 4 | 5 | /** 6 | * @author adyliu (imxylz@gmail.com) 7 | * @since 2016-06-28 8 | */ 9 | public class SidWorkerDemo { 10 | public static void main(String[] args) { 11 | long st = System.currentTimeMillis(); 12 | final int max = 100000; 13 | for (int i = 0; i < max; i++) { 14 | SidWorker.nextSid(); 15 | } 16 | long et = System.currentTimeMillis(); 17 | System.out.println(1000 * max / (et - st) + "/s"); 18 | 19 | } 20 | } 21 | --------------------------------------------------------------------------------