ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「工具」ID生成器

2022-01-06 13:03:38  阅读:2669  来源: 互联网

标签:return String 生成器 long rnd char length 工具 ID


简单生成器

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

/**
 * @version 1.0.0
 * @ClassName IdUtil.java
 * @Description 随机数生成
 */
public final class IdUtil {

    private static final String DATE_PATTERN = "yyyyMMdd";

    private IdUtil() {
    }

    /**
     * 生成18位数字
     * 说明:循环10万左右会有重复ID
     *
     * @return
     */
    public static Long generate18Number() {
        //随机生成一位整数
        int random = (int) (Math.random() * 9 + 1);
        String prefix = String.valueOf(random);
        //格式化日期,生成8位数字
        String middle = LocalDate.now().format(DateTimeFormatter.ofPattern(DATE_PATTERN));
        //生成uuid的hashCode值
        int hashCode = UUID.randomUUID().toString().hashCode();
        //可能为负数
        if (hashCode < 0) {
            hashCode = -hashCode;
        }
        String code = String.valueOf(hashCode);
        if (code.length() > 9) {
            hashCode = Integer.parseInt(code.substring(1));
        }
        String value = prefix + middle + String.format("%09d", hashCode);
        return Long.valueOf(value);
    }
}

基于雪花算法实现

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName TidGenerator.java
 * @Description 可以与Spring结合给该类添加@Component注解,确保只有一个实例
 */
public class TidGenerator {
    /**
     * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
     */
    private final static long twepoch = 1626923889730L;

    /**
     * 机器标识位数
     */
    private final static long workerIdBits = 5L;
    /**
     * 数据中心标识位数
     */
    private final static long dataCenterIdBits = 5L;
    /**
     * 机器ID最大值
     */
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    /**
     * 数据中心ID最大值
     */
    private final static long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
    /**
     * 毫秒内自增位
     */
    private final static long sequenceBits = 12L;
    /**
     * 机器ID偏左移12位
     */
    private final static long workerIdShift = sequenceBits;
    /**
     * 数据中心ID左移17位
     */
    private final static long dataCenterIdShift = sequenceBits + workerIdBits;
    /**
     * 时间毫秒左移22位
     */
    private final static long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /**
     * 上次生产id时间戳
     */
    private static long lastTimestamp = -1L;
    /**
     * 0,并发控制
     */
    private long sequence = 0L;

    private final long workerId;
    /**
     * 数据标识id部分
     */
    private final long dataCenterId;

    /**
     * 默认的TID长度
     */
    private final static int defaultTidLength = 12;

    private static final String SPECIAL_CHARS = "!@#$%^&*_=+-/";

    public TidGenerator() {
        this.dataCenterId = getDataCenterId(maxDataCenterId);
        this.workerId = getMaxWorkerId(dataCenterId, maxWorkerId);
    }

    /**
     * @param workerId     工作机器ID
     * @param dataCenterId 序列号
     */
    public TidGenerator(long workerId, long dataCenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("data center Id can't be greater than %d or less than 0", maxDataCenterId));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
    }

    /**
     * 获取下一个ID
     *
     * @return
     */
    public synchronized long nextSequenceId() {
        long timestamp = timeGen();

        //检查是否出现了「时钟回拨」问题
        timestamp = checkClockMovedBackwards(timestamp);

        if (lastTimestamp == timestamp) {
            // 当前毫秒内,则+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了,则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID,并返回ID
        long nextId = ((timestamp - twepoch) << timestampLeftShift)
                | (dataCenterId << dataCenterIdShift)
                | (workerId << workerIdShift) | sequence;

        return nextId;
    }

    /**
     * 产生下一个TID
     */
    public synchronized String nextTid() {
        //1. 生成一个有序序列
        long sequence = nextSequenceId();
        //2. 转化为62进制
        String str = this.decimalToSixtyTwo(sequence);
        //3. 补全12位并返回
        return completeDigits(str, defaultTidLength);
    }

    /**
     * 产生下一个指定长度的TID
     */
    public synchronized String nextTid(int length) {
        //1. 生成一个有序序列
        long sequence = nextSequenceId();
        //2. 转化为62进制
        String str = this.decimalToSixtyTwo(sequence);
        //3. 补全length位并返回
        return completeDigits(str, length);
    }

    /**
     * 获取至少18位的整数
     *
     * @return
     */
    public synchronized long getAtLeast18BitSequenceId() {
        long seqId = this.nextSequenceId();
        String seq = String.valueOf(seqId);
        if (seq.length() >= 18) {
            return seqId;
        }
        if (seq.length() < 18) {
            //补全位数
            //随机生成一位整数
            int random = (int) (Math.random() * 9 + 1);
            String prefix = String.valueOf(random);
            String value = prefix + String.format("%017d", seqId);
            return Long.parseLong(value);
        }
        return seqId;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

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

    /**
     * 获取 maxWorkerId
     */
    protected static long getMaxWorkerId(long dataCenterId, long maxWorkerId) {
        StringBuffer mpId = new StringBuffer();
        mpId.append(dataCenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
            /*
             * GET jvmPid
             */
            mpId.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpId.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * 数据标识id部分
     */
    protected static long getDataCenterId(long maxDataCenterId) {
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                id = ((0x000000FF & (long) mac[mac.length - 1])
                        | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                id = id % (maxDataCenterId + 1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return id;
    }

    /**
     * 检查是否出现了「时钟回拨」问题
     */
    private long checkClockMovedBackwards(long nowStamp) {
        if (nowStamp >= lastTimestamp) {
            return nowStamp;
        }

        //如果因为某些原因出现了「时钟回拨」问题
        long diff = lastTimestamp - nowStamp;

        //如果时间回拨超过1秒钟,则对外抛出异常,否则暂停1秒钟后再次尝试获取
        if (diff >= TimeUnit.SECONDS.toMillis(1)) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", diff));
        }

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException("An error occurred here", e);
        }

        nowStamp = timeGen();
        if (nowStamp >= lastTimestamp) {
            return nowStamp;
        }

        //如果还出现这个问题,则抛出异常
        throw new RuntimeException(String.format("Clock moved backwards again. Refusing to generate id for %d milliseconds", (lastTimestamp - nowStamp)));
    }

    /**
     * 补全字符串位数(最后返回length位)
     *
     * @param str 原始字符串
     * @return java.lang.String
     */
    protected String completeDigits(String str, int length) {
        if (str.length() >= length) {
            return str;
        }
        int diff = length - str.length() - 1;
        //长度标识 + 1位的随机数(可能不存在) + 原递增序列
        return this.decimalToSixtyTwo(str.length()) + this.randomChars(diff, false) + str;
    }

    /**
     * 十进制转62进制(仅限正整数)
     *
     * @param num 十进制数字
     * @return java.lang.String
     */
    protected String decimalToSixtyTwo(long num) {
        if (num <= 0) {
            return "0";
        }

        StringBuilder sb = new StringBuilder();
        //余数
        long remainder;

        while (num > 0) {
            remainder = num % 62;

            //0-9
            if (remainder < 10) {
                sb.append((char) ('0' + remainder));
            }
            //A-Z
            else if (remainder < 36) {
                sb.append((char) ('A' + remainder - 10));
            }
            //a-z
            else {
                sb.append((char) ('a' + remainder - 36));
            }

            num = num / 62;
        }

        //因为在上面的循环过程中,后一次循环本应是计算出来的高位字符,但是却被我们放在字符串的最后面,因此最终结果需要再反转一次
        return sb.reverse().toString();
    }

    /**
     * 生成指定位数的随机数
     *
     * @param length              长度
     * @param containSpecialChars 是否包含特殊字符
     */
    protected String randomChars(int length, boolean containSpecialChars) {
        char[] chars = new char[length];
        Random rnd = new Random();

        //1. 填补空白位置的字符
        for (int i = 0; i < length; i++) {
            if (chars[i] == 0) {
                chars[i] = (containSpecialChars ? nextCharWithSpecialChars(rnd) : nextChar(rnd));
            }
        }

        //2. 返回结果
        return new String(chars);
    }

    /**
     * 返回一个随机的字符(包含特殊字符)
     */
    private char nextCharWithSpecialChars(Random rnd) {
        switch (rnd.nextInt(4)) {
            case 0:
                return (char) ('a' + rnd.nextInt(26));
            case 1:
                return (char) ('A' + rnd.nextInt(26));
            case 2:
                return (char) ('0' + rnd.nextInt(10));
            default:
                return SPECIAL_CHARS.charAt(rnd.nextInt(SPECIAL_CHARS.length()));
        }
    }

    /**
     * 返回一个随机的字符
     */
    private char nextChar(Random rnd) {
        switch (rnd.nextInt(3)) {
            case 0:
                return (char) ('a' + rnd.nextInt(26));
            case 1:
                return (char) ('A' + rnd.nextInt(26));
            default:
                return (char) ('0' + rnd.nextInt(10));
        }
    }
}

 

标签:return,String,生成器,long,rnd,char,length,工具,ID
来源: https://www.cnblogs.com/xfeiyun/p/15770624.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有