Java生成分布式雪花Id

文章目录

第三方工具类

pom 复制代码
   		<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.6</version>
        </dependency>

代码示例

java 复制代码
   @Test
    public void test(){
        System.out.println(IdUtil.getSnowflakeNextId());
    }

API

java 复制代码
package cn.hutool.core.util;

import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.lang.Singleton;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.lang.id.NanoId;
import cn.hutool.core.net.NetUtil;

public class IdUtil {
    public IdUtil() {
    }

    public static String randomUUID() {
        return UUID.randomUUID().toString();
    }

    public static String simpleUUID() {
        return UUID.randomUUID().toString(true);
    }

    public static String fastUUID() {
        return UUID.fastUUID().toString();
    }

    public static String fastSimpleUUID() {
        return UUID.fastUUID().toString(true);
    }

    public static String objectId() {
        return ObjectId.next();
    }

    /** @deprecated */
    @Deprecated
    public static Snowflake createSnowflake(long workerId, long datacenterId) {
        return new Snowflake(workerId, datacenterId);
    }

    public static Snowflake getSnowflake(long workerId, long datacenterId) {
        return (Snowflake)Singleton.get(Snowflake.class, new Object[]{workerId, datacenterId});
    }

    public static Snowflake getSnowflake(long workerId) {
        return (Snowflake)Singleton.get(Snowflake.class, new Object[]{workerId});
    }

    public static Snowflake getSnowflake() {
        return (Snowflake)Singleton.get(Snowflake.class, new Object[0]);
    }

    public static long getDataCenterId(long maxDatacenterId) {
        Assert.isTrue(maxDatacenterId > 0L, "maxDatacenterId must be > 0", new Object[0]);
        if (maxDatacenterId == Long.MAX_VALUE) {
            --maxDatacenterId;
        }

        long id = 1L;
        byte[] mac = null;

        try {
            mac = NetUtil.getLocalHardwareAddress();
        } catch (UtilException var6) {
        }

        if (null != mac) {
            id = (255L & (long)mac[mac.length - 2] | 65280L & (long)mac[mac.length - 1] << 8) >> 6;
            id %= maxDatacenterId + 1L;
        }

        return id;
    }

    public static long getWorkerId(long datacenterId, long maxWorkerId) {
        StringBuilder mpid = new StringBuilder();
        mpid.append(datacenterId);

        try {
            mpid.append(RuntimeUtil.getPid());
        } catch (UtilException var6) {
        }

        return (long)(mpid.toString().hashCode() & '\uffff') % (maxWorkerId + 1L);
    }

    public static String nanoId() {
        return NanoId.randomNanoId();
    }

    public static String nanoId(int size) {
        return NanoId.randomNanoId(size);
    }

    public static long getSnowflakeNextId() {
        return getSnowflake().nextId();
    }

    public static String getSnowflakeNextIdStr() {
        return getSnowflake().nextIdStr();
    }
}

生成18位雪花Id

java 复制代码
public class SnowFlake {
    // 起始的时间戳,这个时间戳可以是你的系统初始时间,一般取当前时间戳
    private final static long START_TIMESTAMP = 1672502400000L; // 2023-01-01 00:00:00

    // 每一部分占用的位数,可以根据自己的需求进行调整,这里是按照默认的占位数进行分配
    private final static long SEQUENCE_BIT = 12; // 序列号占用的位数
    private final static long MACHINE_BIT = 5; // 机器标识占用的位数
    private final static long DATA_CENTER_BIT = 5; // 数据中心占用的位数

    // 每一部分的最大值,可以根据占用的位数进行计算得到
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
    private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    private final static long MAX_DATA_CENTER_NUM = ~(-1L << DATA_CENTER_BIT);

    // 每一部分向左的位移,计算出来的值是为了后面生成 ID 做准备
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;

    private long dataCenterId; // 数据中心 ID
    private long machineId; // 机器 ID
    private long sequence = 0L; // 序列号
    private long lastTimeStamp = -1L; // 上一次时间戳

    /**
     * <h2>构造方法</h2>
     *
     * @param dataCenterId 数据中心 ID
     * @param machineId    机器 ID
     */
    public SnowFlake(long dataCenterId, long machineId) {
        if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {
            throw new IllegalArgumentException("数据中心标识不能大于等于 " + MAX_DATA_CENTER_NUM + " 或小于 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("机器标识不能大于等于 " + MAX_MACHINE_NUM + " 或小于 0");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    /**
     * <h2>雪花算法核心方法</h2>
     * 通过调用 nextId() 方法,让当前这台机器上的 snowflake 算法程序生成一个全局唯一的 id
     */
    public synchronized long nextId() {
        // 获取系统当前时间戳
        long currentTimeStamp = getSystemCurrentTimeMillis();
        if (currentTimeStamp < lastTimeStamp) {
            throw new RuntimeException("时钟向后移动,拒绝生成雪花算法ID");
        }

        if (currentTimeStamp == lastTimeStamp) {
            // 当前毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 序列号超出范围,需要等待下一毫秒
            if (sequence == 0L) {
                // 获取下一毫秒
                currentTimeStamp = getNextMill(lastTimeStamp);
            }
        } else {
            // 不同毫秒内,序列号置为 0
            sequence = 0L;
        }

        lastTimeStamp = currentTimeStamp;

        // 使用位运算生成最终的 ID
        return (currentTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT
                | dataCenterId << DATA_CENTER_LEFT
                | machineId << MACHINE_LEFT
                | sequence;
    }

    /**
     * <h2>获取系统当前时间戳</h2>
     *
     * @return 当前时间(毫秒)
     */
    private long getSystemCurrentTimeMillis() {
        return System.currentTimeMillis();
    }

    /**
     * <h2>获取下一毫秒</h2>
     * 当某一毫秒的时间,产生的 id 数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生 ID
     *
     * @param lastTimestamp 上次生成 ID 的时间截
     * @return 当前时间戳
     */
    private long getNextMill(long lastTimestamp) {
        long timeMillis = getSystemCurrentTimeMillis();
        while (timeMillis <= lastTimestamp) {
            timeMillis = getSystemCurrentTimeMillis();
        }
        return timeMillis;
    }

    public static void main(String[] args) {
        SnowFlake worker1 = new SnowFlake(1, 1);
        System.out.println(worker1.nextId());
    }
}

生成13位雪花Id

java 复制代码
package com.ais.common.web.utils;

public class IdUtil {
    /**
     * 开始时间截 (本次时间戳为:Thu Nov 04 2010 09:42:54 GMT+0800 (中国标准时间)----1288834974657L---1656543015264587776--19 )
     */
    private final long startTime = 1683803335498L;

    /**
     * 机器id所占的位数
     */
    private final long workerIdBits = 3L;

    /**
     * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 序列在id中占的位数
     */
    private final long sequenceBits = 5L;

    /**
     * 机器ID向左移12位
     */
    private final long workerIdShift = sequenceBits;

    /**
     * 时间截向左移22位(10+12)
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits;

    /**
     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 工作机器ID(0~1024)
     */
    private long workerId;

    /**
     * 毫秒内序列(0~4095)
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的时间截
     */
    private long lastTimestamp = -1L;
    private final static IdUtil idWorker = new IdUtil(1);
    //==============================Constructors=====================================

    /**
     * 构造函数
     *
     * @param workerId 工作ID (0~1024)
     */
    public IdUtil(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
        }
        this.workerId = workerId;
    }

    // ==============================Methods==========================================

    /**
     * 获得下一个ID (该方法是线程安全的)
     *
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - startTime) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     *
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     *
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    public static long getSnowflakeNextId() {
        return idWorker.nextId();
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        System.out.println(IdUtil.getSnowflakeNextId());
    }
}
相关推荐
ok!ko3 小时前
设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
java·设计模式·原型模式
2402_857589363 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
吾爱星辰4 小时前
Kotlin 处理字符串和正则表达式(二十一)
java·开发语言·jvm·正则表达式·kotlin
哎呦没5 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
编程、小哥哥5 小时前
netty之Netty与SpringBoot整合
java·spring boot·spring
IT学长编程6 小时前
计算机毕业设计 玩具租赁系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·玩具租赁系统
莹雨潇潇6 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
杨哥带你写代码6 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
郭二哈7 小时前
C++——模板进阶、继承
java·服务器·c++
A尘埃7 小时前
SpringBoot的数据访问
java·spring boot·后端