雪花算法自动分配workerId & datacenterId
解决雪花算法workerId和datacenterId的自动分配问题。
前言
分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成,这时候通常我们会选择使用雪花算法
snowflake的结构如下(每部分用-分开):
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年) 然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) 最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)。
一般创建雪花算法的时候,依赖:
Params:
workerId – 终端ID
datacenterId – 数据中心ID
两个参数,这两个值的取值都为 0~31 之间的整型。
提供了一个基于Redis的实现方式:
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);
}
}
最后修改于 2021-02-02
此篇文章的评论功能已经停用。