雪花算法自动分配workerId & datacenterId

前言

分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成,这时候通常我们会选择使用雪花算法

1
2
snowflake的结构如下(每部分用-分开):
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年) 然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) 最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)。

一般创建雪花算法的时候,依赖:

1
2
3
Params:
workerId – 终端ID
datacenterId – 数据中心ID

两个参数,这两个值的取值都为 0~31 之间的整型。

提供了一个基于Redis的实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
public class SnowflakeInitiator {
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SnowIdDto implements Serializable, Comparable<SnowIdDto> {
/**
* 注册时的时间戳
*/
private Long timestamp;
/**
* 数据中心节点 0~31
*/
private Integer dataCenterId;
/**
* 工作节点 0~31
*/
private Integer workerId;
@Override
public int compareTo(SnowIdDto o) {
long ex = this.timestamp - o.getTimestamp();
return ex > 0 ? 1 : -1;
}
}
private static final Integer DATA_SIZE = 32;
private static final String[] RADIX_STR = new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v"};
private static Map<String, Integer> RADIX_MAP = new LinkedHashMap<>();
private static final String SNOW = "RedisPrefix";
static {
for (int i = 0; i < DATA_SIZE; i++) {
RADIX_MAP.put(RADIX_STR[i], i);
}
}
/**
* 计算雪花算法参数的新算法
*
* @param redisTemplate
* @param appName
* @return
*/
private SnowIdDto calculateDataIdAndWorkId(RedisTemplate redisTemplate, String appName) {
String key = SNOW + appName;
RedisAtomicLong redisAtomicLong = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
long andIncrement = redisAtomicLong.getAndIncrement();
long result = andIncrement % (DATA_SIZE * DATA_SIZE);
String str = StringUtils.leftPad(Integer.toString(Math.toIntExact(result), DATA_SIZE), 2, "0");
Integer dataId = RADIX_MAP.get(str.substring(0, 1));
Integer workId = RADIX_MAP.get(str.substring(1, 2));
return new SnowIdDto(System.currentTimeMillis(), dataId, workId);
}
}
----本文结束 感谢您的阅读----