- 🤔 目录 🍟 -
雪花算法自动分配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

此篇文章的评论功能已经停用。