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