Skip to content

Commit 981abd2

Browse files
author
bob
committed
添加订单号生成器项目-分布式id雪花算法
1 parent 58272fb commit 981abd2

File tree

3 files changed

+226
-0
lines changed

3 files changed

+226
-0
lines changed

commons-orderno-generator/pom.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>com.bob.commons</groupId>
8+
<artifactId>commons-parent</artifactId>
9+
<version>${bob.project.version}</version>
10+
<relativePath>../commons-parent/pom.xml</relativePath>
11+
</parent>
12+
<artifactId>commons-orderno-generator</artifactId>
13+
<version>1.0.1</version>
14+
<packaging>jar</packaging>
15+
16+
<description>
17+
订单号生成器jar包
18+
1.雪花算法
19+
</description>
20+
21+
</project>
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.bob.snowflake;
2+
3+
/**
4+
* twitter的snowflake算法 -- java实现
5+
* <p>
6+
* 原理:
7+
* SnowFlake算法产生的ID是一个64位的整型,结构如下(每一部分用“-”符号分隔):
8+
* <p>
9+
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
10+
* 二进制格式:(64位)
11+
* 1位标识部分+41位时间戳部分+5位数据中心标识部分+5位机器标识部分+12位序列号部分
12+
* <p>
13+
* 1位标识部分:在java中由于long的最高位是符号位,正数是0,负数是1,一般生成的ID为正数,所以为0;
14+
* 41位时间戳部分:这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差值(当前时间-固定的开始时间),
15+
* 这样可以使产生的ID从更小值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年;
16+
* 10位节点部分:Twitter实现中使用前5位作为数据中心标识,后5位作为机器标识,可以部署1024个节点;
17+
* 12位序列号部分:支持同一毫秒内同一个节点可以生成4096个ID;
18+
* <p>
19+
* SnowFlake算法生成的ID大致上是按照时间递增的,用在分布式系统中时,需要注意数据中心标识和机器标识必须唯一,
20+
* 这样就能保证每个节点生成的ID都是唯一的。或许我们不一定都需要像上面那样使用5位作为数据中心标识,5位作为机器标识,
21+
* 可以根据我们业务的需要,灵活分配节点部分,
22+
* 如:若不需要数据中心,完全可以使用全部10位作为机器标识;若数据中心不多,也可以只使用3位作为数据中心,7位作为机器标识。
23+
* <p>
24+
* 注:long类型数据为8字节,一个字节8位,刚好64位
25+
* <p>
26+
*
27+
* @author bobyang
28+
* @version v1.0
29+
* @date 2017/09/02
30+
*/
31+
public class Snowflake {
32+
33+
/**
34+
* 起始的时间戳
35+
*/
36+
private static final long START_STMP = 1504364544760L;//2017年09月02日23:02:47
37+
38+
/**
39+
* 每一部分占用的位数
40+
*/
41+
private static final long DATACENTER_BIT = 5;//数据中心占用的位数
42+
private static final long MACHINE_BIT = 5; //机器标识占用的位数
43+
private static final long SEQUENCE_BIT = 12; //序列号占用的位数
44+
45+
/**
46+
* 每一部分的最大值
47+
*/
48+
private static final long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
49+
private static final long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
50+
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
51+
52+
/**
53+
* 每一部分向左的位移
54+
*/
55+
private static final long MACHINE_LEFT = SEQUENCE_BIT;
56+
private static final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
57+
private static final long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
58+
59+
private long datacenterId; //数据中心
60+
private long machineId; //机器标识
61+
private long sequence = 0L; //序列号
62+
private long lastStmp = -1L;//上一次时间戳
63+
64+
public Snowflake(long datacenterId, long machineId) {
65+
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
66+
throw new IllegalArgumentException("DataCenterId can't be greater than " + MAX_DATACENTER_NUM + "or less than 0");
67+
}
68+
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
69+
throw new IllegalArgumentException("MachineId can't be greater than " + MAX_MACHINE_NUM + " or less than 0");
70+
}
71+
this.datacenterId = datacenterId;
72+
this.machineId = machineId;
73+
}
74+
75+
/**
76+
* 产生下一个ID
77+
*
78+
* @return
79+
*/
80+
public synchronized long nextId() {
81+
long currStmp = getNewstmp();
82+
if (currStmp < lastStmp) {
83+
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
84+
}
85+
if (currStmp == lastStmp) {
86+
//相同毫秒内,序列号自增
87+
sequence = (sequence + 1) & MAX_SEQUENCE;
88+
//同一毫秒的序列数已经达到最大
89+
if (sequence == 0L) {
90+
currStmp = getNextMill();
91+
}
92+
} else {
93+
//不同毫秒内,序列号置为0
94+
sequence = 0L;
95+
}
96+
lastStmp = currStmp;
97+
return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
98+
| datacenterId << DATACENTER_LEFT //数据中心部分
99+
| machineId << MACHINE_LEFT //机器标识部分
100+
| sequence; //序列号部分
101+
}
102+
103+
/**
104+
* 获取下一个毫秒值
105+
*
106+
* @return
107+
*/
108+
private long getNextMill() {
109+
long mill = getNewstmp();
110+
while (mill <= lastStmp) {
111+
mill = getNewstmp();
112+
}
113+
return mill;
114+
}
115+
116+
/**
117+
* 获取新的毫秒值
118+
*
119+
* @return
120+
*/
121+
private long getNewstmp() {
122+
return System.currentTimeMillis();
123+
}
124+
125+
//测试
126+
public static void main(String[] args) {
127+
Snowflake snowFlake = new Snowflake(22, 30);//48130544695631940
128+
for (int i = 0; i < (1 << 12); i++) {
129+
System.out.println(snowFlake.nextId());
130+
}
131+
System.out.println(~(-1L << DATACENTER_BIT));
132+
System.out.println(1 << 12);
133+
}
134+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.bob.snowflake;
2+
3+
/**
4+
* 无数据中心版
5+
* <p>
6+
* 即64位格式如下
7+
* 1位标识部分+41位时间戳部分+10位机器标识部分+12位序列号部分
8+
*
9+
* @author bobyang
10+
* @version v1.0
11+
* @date 2017/09/02
12+
*/
13+
public class Snowflake2 {
14+
15+
public static final int NODE_SHIFT = 10;
16+
public static final int SEQ_SHIFT = 12;
17+
18+
public static final short MAX_NODE = 1024;
19+
public static final short MAX_SEQUENCE = 4096;
20+
21+
private short sequence;
22+
private long referenceTime;
23+
24+
private int node;
25+
26+
/**
27+
* A snowflake is designed to operate as a singleton instance within the context of a node.
28+
* If you deploy different nodes, supplying a unique node id will guarantee the uniqueness
29+
* of ids generated concurrently on different nodes.
30+
*
31+
* @param node This is an id you use to differentiate different nodes.
32+
*/
33+
public Snowflake2(int node) {
34+
if (node < 0 || node > MAX_NODE) {
35+
throw new IllegalArgumentException(String.format("node must be between %s and %s", 0, MAX_NODE));
36+
}
37+
this.node = node;
38+
}
39+
40+
/**
41+
* Generates a k-ordered unique 64-bit integer. Subsequent invocations of this method will produce
42+
* increasing integer values.
43+
*
44+
* @return The next 64-bit integer.
45+
*/
46+
public long next() {
47+
48+
long currentTime = System.currentTimeMillis();
49+
long counter;
50+
51+
synchronized (this) {
52+
53+
if (currentTime < referenceTime) {
54+
throw new RuntimeException(String.format("Last referenceTime %s is after reference time %s", referenceTime, currentTime));
55+
} else if (currentTime > referenceTime) {
56+
this.sequence = 0;
57+
} else {
58+
if (this.sequence < MAX_SEQUENCE) {
59+
this.sequence++;
60+
} else {
61+
throw new RuntimeException("Sequence exhausted at " + this.sequence);
62+
}
63+
}
64+
counter = this.sequence;
65+
referenceTime = currentTime;
66+
}
67+
68+
return currentTime << NODE_SHIFT << SEQ_SHIFT | node << SEQ_SHIFT | counter;
69+
}
70+
71+
}

0 commit comments

Comments
 (0)