JAVA makes wheels - generate distributed global unique ID

Note: the content of this code comes from Internet reprint and arrangement.

SnowFlake algorithm is an open-source distributed id generation algorithm of Twitter.

1. twitter's SnowFlake generation ID can be generated in order according to time
2. The result of id generated by SnowFlake algorithm is a 64 bit integer.
3. Duplicate IDS will not be generated in the distributed system (differentiated by datacenterId and machineId)

/**
 *The @ Description snowflake algorithm is a classic algorithm for generating distributed globally unique ID s
 * Twitter_Snowflake<br>
 *The structure of SnowFlake is as follows (each part is separated with -): < br >
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
 *1-bit identifier. Since the long basic type is signed in Java, the highest bit is the sign bit, the positive number is 0, and the negative number is 1, so the id is generally a positive number, and the highest bit is 0 < br >
 *41 bit time cut (millisecond level), note that 41 bit time cut is not the time cut to store the current time, but the difference between the storage time cut (current time cut start time cut)
 *The start time section here is generally the start time of our id generator, which is specified by our program (as follows: the startTime property of IdWorker class of the program below). The 41 bit time period can be used for 69 years, and the year t = (1L < < 41) / (1000L * 60 * 60 * 24 * 365) = 69 < br >
 *The 10 bit data machine bit can be deployed in 1024 nodes, including 5-bit datacenterId and 5-bit workerid < br >
 *The 12 bit sequence, counting in milliseconds, and the 12 bit counting sequence number support each node to generate 4096 ID sequence numbers per millisecond (the same machine, the same time cut) < br >
 *Add up to just 64 bits, a Long type. <br>
 *The advantage of SnowFlake is that it is sorted according to the increasing time on the whole, and there is no ID collision in the whole distributed system (distinguished by data center ID and machine ID), and it is highly efficient. After testing, SnowFlake can generate about 260000 IDS per second.
 */

Java code

public class IdWorkerUtils {

    // ==============================Fields===========================================
    /** Start time (2015-01-01) */
    private final long twepoch = 1489111610226L;

    /** Number of digits occupied by machine id */
    private final long workerIdBits = 5L;

    /** Number of digits occupied by data id */
    private final long dataCenterIdBits = 5L;

    /** The maximum machine id supported, the result is 31 (this shift algorithm can quickly calculate the maximum decimal number that several binary numbers can represent) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** Maximum data id supported, result is 31 */
    private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);

    /** Number of digits of sequence in id */
    private final long sequenceBits = 12L;

    /** Machine ID moved 12 bits to the left */
    private final long workerIdShift = sequenceBits;

    /** Move data id 17 bits to the left (12 + 5) */
    private final long dataCenterIdShift = sequenceBits + workerIdBits;

    /** Time cut 22 bits left (5 + 5 + 12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    /** Mask for generating sequence, here is 4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** Working machine ID (0-31) */
    private long workerId;

    /** Data center ID (0-31) */
    private long dataCenterId;

    /** Sequence in milliseconds (0-4095) */
    private long sequence = 0L;

    /** Last ID generated by */
    private long lastTimestamp = -1L;

    private static IdWorkerUtils idWorker;

    static {
        idWorker = new IdWorkerUtils(getWorkId(),getDataCenterId());
    }

    //==============================Constructors=====================================
    /**
     * Constructor
     * @param workerId Work ID (0-31)
     * @param dataCenterId Data center ID (0-31)
     */
    public IdWorkerUtils(long workerId, long dataCenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
        }
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("dataCenterId can't be greater than %d or less than 0", maxDataCenterId));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
    }

    // ==============================Methods==========================================
    /**
     * Get the next ID (this method is thread safe)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //If the current time is less than the time stamp generated by the last ID, an exception should be thrown when the system clock goes back.
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //If generated at the same time, sequence in milliseconds
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //Sequence overflow in MS
            if (sequence == 0) {
                //Block to next MS, get new timestamp
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //Timestamp change, sequence reset in MS
        else {
            sequence = 0L;
        }

        //Last ID generated by
        lastTimestamp = timestamp;

        //Shift and combine by or operation to form a 64 bit ID
        return ((timestamp - twepoch) << timestampLeftShift)
                | (dataCenterId << dataCenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }

    /**
     * Blocks to the next millisecond until a new timestamp is obtained
     * @param lastTimestamp Last ID generated by
     * @return Current timestamp
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * Returns the current time in milliseconds
     * @return Current time (MS)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    private static Long getWorkId(){
        try {
            String hostAddress = Inet4Address.getLocalHost().getHostAddress();
            int[] ints = StringUtils.toCodePoints(hostAddress);
            int sums = 0;
            for(int b : ints){
                sums += b;
            }
            return (long)(sums % 32);
        } catch (UnknownHostException e) {
            // If acquisition fails, use random number standby
            return RandomUtils.nextLong(0,31);
        }
    }

    private static Long getDataCenterId(){
        int[] ints = StringUtils.toCodePoints(SystemUtils.getHostName());
        int sums = 0;
        for (int i: ints) {
            sums += i;
        }
        return (long)(sums % 32);
    }


    /**
     * Static tool class
     *
     * @return
     */
    public static Long generateId(){
        long id = idWorker.nextId();
        return id;
    }

    //==============================Test=============================================
    /** test */
    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());
        long startTime = System.nanoTime();
        for (int i = 0; i < 50000; i++) {
            long id = IdWorkerUtils.generateId();
            System.out.println(id);
        }
        System.out.println((System.nanoTime()-startTime)/1000000+"ms");
    }
}

 

Explain:

People who have done projects know that a lot of written reusable code blocks or useful tool classes have not been sorted out. When they need to, they have to turn on the project to find out. Although it is not difficult to develop the function, they have to spend time and cost to write and test. This kind of repeated wheel building is too many times. Therefore, it is better to keep the wheel for everyone to use.

1. This wheel can have: this component does not exist when it needs to be used.
2. When I need it, the wheel is not there: every technology or tool generation has its project background. When the code is written in the project, I know that there is this thing. When I change a project or company, there is no backup or record. At this time, you are not there, and you have to take the time to manually hit it again;
3. I don't know whether to make wheels or not: in most cases, it's hard for beginners to know whether they are making wheels repeatedly. In fact, making wheels is not my purpose. My goal is to complete the task. The faster the task is completed, the better the quality is. Instead of judging whether you're making wheels.
4. Don't want to spend time building wheels repeatedly: sometimes you will encounter something that is not difficult but takes up a lot of time. Of course, there are existing wheels that take the least time;
5. I just want to learn wheels: beginners do not build wheels repeatedly, but improve their knowledge and skills after learning.

Wheels have been tested, but there are inevitably mistakes. If there are mistakes, please point out.

Keywords: Programming less Java

Added by msarefin on Tue, 29 Oct 2019 04:57:07 +0200