全局唯一Id生成—基于类Snowflake算法

全局唯一Id生成—基于类Snowflake算法

1.方法介绍

  • 简介:
    • 算法核心是利用64bits的二进制位表示一个id;并将64位分为几段,分别表示不同的值,如时间戳、机器id、自增序号的等。

2.代码:

/**
 * payment-api生成id 使用服务器ip地址、端口等信息组合;
 * 生成随机的id
 * 采用雪花算法,64 bits的长整型数,生成不重复id;首位为0,表正数;
 * 0|000 0000 0000 0000 0000 0000 0000 0000 0000 0000 00|00| 0000 0000| 0000 0000 0000
 * 正数位|          时间戳 41bits                        |ipNum 8bits|同一毫秒下的自增数 12bits;4095最大
 * 正数位|          时间戳 41bits                        |内部外部 2bits|          |同一毫秒下的自增数 12bits;4095最大
 */
@Slf4j
@Component //把普通pojo实例化到spring容器中
public class ApiEasyIdGenerateUtil {


    //初始化
    @PostConstruct
    private void init() {
        apiEasyIdGenerateUtil = this;
        this.ipNum  = getIpNum();
    }

    private Long getIpNum() {
        String[] split = apiEasyIdGenerateUtil.ipAddress.split("\\.");
        String ipNumString= split[split.length-1];
        Long ipNum = Long.valueOf(ipNumString);
        ipNum %=255L;
        return ipNum;
    }

    @Value("${spring.cloud.client.ip-address}")
    private String ipAddress;

    private static ApiEasyIdGenerateUtil apiEasyIdGenerateUtil;


    private Long ipNum ;//8位,0~255
    private Long workerId = 1L;//2位,0~3  内部服务为0,外部服务为1;
    //默认的时间戳 北京时间:2021-08-08 08:08:08  发发发~
    private Long defaultTimeStamp = 1628381288000L;
    //时间戳处在的位置,即需要向右移动多少位;22位
    private Long timeStampMoveBits = 22L;
    //ipNum 处在的位置,即需要向右移动多少位;12位
    private Long ipNumMoveBits = 12L;
    private Long workerIdMoveBits = 20L;
    //同一毫秒下自增数;
    private Long sequence;
    //序列号最大值12个零,与sequence按位与,获得小于等于最大值的数;  0000 .... 1111
    private long sequenceMask = -1L ^ (-1L << ipNumMoveBits);//sequenceMask :0000 .... 1111

    //用来判断是否同一秒毫秒出现两次请求
    private long lastTimestamp = -1L;




    private static long getTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 生成随机的id
     * UUID 去掉"-"
     *
     * @return
     */
    public static String createNewEasyId() {
        Long nextId = getNextId();
        return nextId.toString();
    }

    /**
     * 下个id的方法
     *
     * @return
     */
    private static Long getNextId() {
        synchronized (ApiEasyIdGenerateUtil.class) {
            long timestampNow = getTimestamp();
            long lastTimestamp = apiEasyIdGenerateUtil.lastTimestamp;
            if (timestampNow < lastTimestamp) {
                log.error("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestampNow) + " milliseconds");
                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                        lastTimestamp - timestampNow));
            }
            //获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。
            if (lastTimestamp == timestampNow) {
                //序列号最大值12个零,与sequence按位与,获得小于等于最大值的数;  0000 .... 1111
                apiEasyIdGenerateUtil.sequence = (apiEasyIdGenerateUtil.sequence + 1) & apiEasyIdGenerateUtil.sequenceMask;
                if (apiEasyIdGenerateUtil.sequence == 0) {
                    timestampNow = waitToNextMills(lastTimestamp);
                }
            } else {
                apiEasyIdGenerateUtil.sequence = 0L;
            }
            apiEasyIdGenerateUtil.lastTimestamp = timestampNow;
            return (timestampNow - apiEasyIdGenerateUtil.defaultTimeStamp ) << apiEasyIdGenerateUtil.timeStampMoveBits |
                    apiEasyIdGenerateUtil.workerId << apiEasyIdGenerateUtil.workerIdMoveBits |
                    apiEasyIdGenerateUtil.ipNum << apiEasyIdGenerateUtil.ipNumMoveBits |
                    apiEasyIdGenerateUtil.sequence;
        }
    }


    /**
     * 等待直到毫秒值大于上次值,并返回现在的毫秒值;
     *
     * @return
     */
    private static long waitToNextMills(long lastTimestamp) {
        long timestampNow = getTimestamp();
        while (timestampNow <= lastTimestamp) {
            timestampNow = getTimestamp();
        }
        return timestampNow;
    }


}

3.测试

@Test
    void testConsumer() throws InterruptedException {

        for (int i = 0; i < 100; i++) {
            System.out.println(ApiEasyIdGenerateUtil.createNewEasyId());
        }

    }

结果:

26879561124810752
26879561124810753
26879561124810754
26879561124810755
26879561124810756
26879561124810757
26879561124810758
26879561124810759
26879561124810760
26879561124810761
26879561124810762
26879561124810763
26879561124810764
26879561124810765
26879561124810766
26879561124810767
26879561124810768
26879561124810769
26879561124810770
26879561124810771
26879561124810772
26879561124810773
26879561124810774
26879561124810775
26879561124810776
26879561124810777
26879561124810778
26879561124810779
26879561124810780
26879561124810781
26879561124810782
26879561124810783
26879561124810784
26879561124810785
26879561124810786
26879561124810787
26879561124810788
26879561124810789
26879561124810790
26879561124810791
26879561124810792
26879561124810793
26879561124810794
26879561124810795
26879561124810796
26879561124810797
26879561124810798
26879561124810799
26879561124810800
26879561124810801
26879561124810802
26879561124810803
26879561124810804
26879561124810805
26879561124810806
26879561124810807
26879561124810808
26879561124810809
26879561124810810
26879561124810811
26879561124810812
26879561124810813
26879561124810814
26879561124810815
26879561124810816
26879561124810817
26879561124810818
26879561124810819
26879561124810820
26879561124810821
26879561124810822
26879561124810823
26879561124810824
26879561124810825
26879561124810826
26879561124810827
26879561124810828
26879561124810829
26879561124810830
26879561124810831
26879561124810832
26879561124810833
26879561124810834
26879561124810835
26879561124810836
26879561124810837
26879561124810838
26879561124810839
26879561124810840
26879561124810841
26879561124810842
26879561124810843
26879561124810844
26879561124810845
26879561124810846
26879561124810847
26879561124810848
26879561124810849
26879561129005056
26879561129005057

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值