Snowflake 分布式有序ID生成

简述

项目使用 spring boot 开发,需要使用 snowflake 算法生成 id,以下为配置及代码。

配置

使用配置文件配置分布式节点信息,此处使用 woker.cfg 文件,并将其放在 tomcat 根目录下。

woker.cfg

datacenterId=1
workerId=1

spring properties :

# 分布式节点配置
distributed.config.file=${catalina.base}/worker.cfg

代码

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "file:${distributed.config.file}")
public class IdHelper {

    private static final Logger logger = LoggerFactory.getLogger(IdHelper.class);

    //数据中心id
    @Value("${datacenterId}")
    private int datacenterId;

    //工作节点id
    @Value("${workerId}")
    private int workerId;

    private SnowFlake snowFlake;

    @PostConstruct
    private void init() {
        logger.info("datacenterId : {}", datacenterId);
        logger.info("workerId : {}", workerId);

        snowFlake = new SnowFlake(datacenterId, workerId);
    }

    /**
     * 获取ID
     * @author sunk
     */
    public synchronized long nextId() {
        return snowFlake.nextId();
    }
}
public class SnowFlake {

    /**
     * 起始的时间戳
     */
    private static final long START_STMP = 1416283932120L;

    private static final long ZERO = 0L;

    /**
     * 每一部分占用的位数
     */
    private static final long SEQUENCE_BIT = 12;  //序列号占用的位数
    private static final long WORKER_BIT = 5;     //工作节点标识占用的位数
    private static final long DATACENTER_BIT = 5; //数据中心占用的位数

    /**
     * 每一部分的最大值
     */
    private static final long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private static final long MAX_WORKER_NUM = -1L ^ (-1L << WORKER_BIT);
    private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

    /**
     * 每一部分向左的位移
     */
    private static final long WORKER_LEFT = SEQUENCE_BIT;
    private static final long DATACENTER_LEFT = SEQUENCE_BIT + WORKER_BIT;
    private static final long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private final long datacenterId;  //数据中心
    private final long workerId;      //工作节点标识
    private long sequence = 0L;       //序列号
    private long lastStmp = -1L;      //上一次时间戳

    public SnowFlake(long datacenterId, long workerId) {

        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException(
                    "datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (workerId > MAX_WORKER_NUM || workerId < 0) {
            throw new IllegalArgumentException(
                    "workerId can't be greater than MAX_WORKER_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.workerId = workerId;
    }

    /**
     * 产生下一个ID
     */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("clock moved backwards, refusing to generate id.");
        }

        if (currStmp == lastStmp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == ZERO) {
                currStmp = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }

        lastStmp = currStmp;

        return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
                | datacenterId << DATACENTER_LEFT       //数据中心部分
                | workerId << WORKER_LEFT               //工作节点标识部分
                | sequence;                             //序列号部分
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }

    /**
     * 获取数据中心ID
     * @author sunk
     */
    public static long getDatacenterId(long id) {

        long shift = id >> DATACENTER_LEFT;
        long mask = (1 << DATACENTER_BIT) - 1;
        long dcid = shift & mask;

        String idBit = Long.toBinaryString(id);
        String shiftBit = Long.toBinaryString(shift);
        String maskBit = Long.toBinaryString(mask);
        String dcidBit = Long.toBinaryString(dcid);

        System.out.println("id :" + id + " | " + idBit);
        System.out.println("shift : " + shift + " | " + shiftBit);
        System.out.println("mask : " + mask + " | " + maskBit);
        System.out.println("dcid : " + dcid + " | " + dcidBit);

        return shift & mask;
    }

    /**
     * 获取工作节点ID
     * @author sunk
     */
    public static long getWorkerId(long id) {

        long shift = id >> WORKER_LEFT;
        long mask = (1 << WORKER_BIT) - 1;
        return shift & mask;
    }
}

参考

1.SnowFlake- Twitter的雪花算法SnowFlake,使用Java语言实现

你可能感兴趣的:(----,----,FW,Spring,----,PL,-,Java)