All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.evento.common.utils.Snowflake Maven / Gradle / Ivy

The newest version!
package com.evento.common.utils;

import java.net.NetworkInterface;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Enumeration;

/**
 * Distributed Sequence Generator. Inspired by Twitter snowflake:
 * ...
 * 

* This class should be used as a Singleton. Make sure that you create and reuse a Single instance of Snowflake per node * in your distributed system cluster. */ public class Snowflake { private static final int UNUSED_BITS = 1; // Sign bit, Unused (always set to 0) private static final int EPOCH_BITS = 41; private static final int NODE_ID_BITS = 10; private static final int SEQUENCE_BITS = 12; private static final long maxNodeId = (1L << NODE_ID_BITS) - 1; private static final long maxSequence = (1L << SEQUENCE_BITS) - 1; private static final long DEFAULT_CUSTOM_EPOCH = 1420070400000L; private final long nodeId; private final long customEpoch; private volatile long lastTimestamp = -1L; private volatile long sequence = 0L; /** * Snowflake is a class that represents a unique identifier generator based on Twitter's snowflake algorithm. * @param nodeId a node id to generate the snowflake * @param customEpoch starting epoch */ // Create Snowflake with a nodeId and custom epoch public Snowflake(long nodeId, long customEpoch) { if (nodeId < 0 || nodeId > maxNodeId) { throw new IllegalArgumentException(String.format("NodeId must be between %d and %d", 0, maxNodeId)); } this.nodeId = nodeId; this.customEpoch = customEpoch; } /** * Snowflake is a class that represents a unique identifier generator based on Twitter's snowflake algorithm. * * @param nodeId The nodeId used to generate the snowflake identifier. */ // Create Snowflake with a nodeId public Snowflake(long nodeId) { this(nodeId, DEFAULT_CUSTOM_EPOCH); } /** * Snowflake is a class that represents a unique identifier generator based on Twitter's snowflake algorithm. */ // Let Snowflake generate a nodeId public Snowflake() { this.nodeId = createNodeId(); this.customEpoch = DEFAULT_CUSTOM_EPOCH; } /** * Generates a unique identifier based on Twitter's snowflake algorithm. * The identifier consists of a timestamp, a node ID, and a sequence number, * which are combined to form a 64-bit long value. * @return the next Snowflake ID */ public synchronized long nextId() { long currentTimestamp = timestamp(); if (currentTimestamp < lastTimestamp) { throw new IllegalStateException("Invalid System Clock!"); } if (currentTimestamp == lastTimestamp) { sequence = (sequence + 1) & maxSequence; if (sequence == 0) { // Sequence Exhausted, wait till next millisecond. currentTimestamp = waitNextMillis(currentTimestamp); } } else { // reset sequence to start with zero for the next millisecond sequence = 0; } lastTimestamp = currentTimestamp; return currentTimestamp << (NODE_ID_BITS + SEQUENCE_BITS) | (nodeId << SEQUENCE_BITS) | sequence; } // Get current timestamp in milliseconds, adjust for the custom epoch. private long timestamp() { return Instant.now().toEpochMilli() - customEpoch; } // Block and wait till next millisecond private long waitNextMillis(long currentTimestamp) { while (currentTimestamp == lastTimestamp) { currentTimestamp = timestamp(); } return currentTimestamp; } private long createNodeId() { long nodeId; try { StringBuilder sb = new StringBuilder(); Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaces.nextElement(); byte[] mac = networkInterface.getHardwareAddress(); if (mac != null) { for (byte macPort : mac) { sb.append(String.format("%02X", macPort)); } } } nodeId = sb.toString().hashCode(); } catch (Exception ex) { nodeId = (new SecureRandom().nextInt()); } nodeId = nodeId & maxNodeId; return nodeId; } /** * Parses a unique identifier generated by the Snowflake class. * The identifier is a 64-bit long value consisting of a timestamp, a node ID, and a sequence number. * * @param id The unique identifier to parse. * @return An array of long values [timestamp, nodeId, sequence]. */ public long[] parse(long id) { long maskNodeId = ((1L << NODE_ID_BITS) - 1) << SEQUENCE_BITS; long maskSequence = (1L << SEQUENCE_BITS) - 1; long timestamp = (id >> (NODE_ID_BITS + SEQUENCE_BITS)) + customEpoch; long nodeId = (id & maskNodeId) >> SEQUENCE_BITS; long sequence = id & maskSequence; return new long[]{timestamp, nodeId, sequence}; } @Override public String toString() { return "Snowflake Settings [EPOCH_BITS=" + EPOCH_BITS + ", NODE_ID_BITS=" + NODE_ID_BITS + ", SEQUENCE_BITS=" + SEQUENCE_BITS + ", CUSTOM_EPOCH=" + customEpoch + ", NodeId=" + nodeId + "]"; } /** * The forInstant method in the Snowflake class generates a unique identifier based on Twitter's snowflake algorithm. * The identifier consists of a timestamp, a node ID, and a sequence number, which are combined to form a 64-bit long value. * * @param minus The Instant object representing the time to subtract from the current time to generate the timestamp. * @return The generated unique identifier as a Long value. */ public Long forInstant(Instant minus) { var timestamp = minus.toEpochMilli() - customEpoch; return timestamp << (NODE_ID_BITS + SEQUENCE_BITS) | (0L); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy