Java案例如何实现雪花算法?

wen java案例 3

本文目录导读:

Java案例如何实现雪花算法?

  1. 雪花算法结构
  2. 基础实现
  3. 增强版实现(推荐)
  4. 使用示例
  5. Spring Boot集成
  6. 高级特性实现
  7. 关键设计要点
  8. 配置建议

我来详细介绍雪花算法的Java实现。

雪花算法结构

雪花算法的ID由64位二进制组成:

  • 1位:符号位(始终为0)
  • 41位:时间戳(毫秒级)
  • 10位:工作机器ID
  • 12位:序列号

基础实现

public class SnowflakeIdGenerator {
    // 基础常量
    private final long twepoch = 1609459200000L; // 2021-01-01 00:00:00
    // 机器ID位数
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    // 序列号位数
    private final long sequenceBits = 12L;
    // 最大值计算
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    // 位移量
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    // 组件值
    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                String.format("Worker ID must be between 0 and %d", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                String.format("Datacenter ID must be between 0 and %d", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    public synchronized long nextId() {
        long timestamp = timeGen();
        // 时钟回拨处理
        if (timestamp < lastTimestamp) {
            long offset = lastTimestamp - timestamp;
            if (offset <= 5) {
                // 等待时间前进
                try {
                    wait(offset * 100);
                } catch (Exception e) {
                    Thread.yield();
                }
                timestamp = timeGen();
                if (timestamp < lastTimestamp) {
                    throw new RuntimeException(
                        String.format("Clock moved backwards. Refusing for %d milliseconds", offset));
                }
            } else {
                throw new RuntimeException(
                    String.format("Clock moved backwards. Refusing for %d milliseconds", offset));
            }
        }
        // 同一毫秒内处理序列号
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 序列号已满,等待下一毫秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // 生成ID
        return ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    private long timeGen() {
        return System.currentTimeMillis();
    }
}

增强版实现(推荐)

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
public class SnowflakeIdGeneratorV2 {
    private final long twepoch = 1609459200000L;
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long sequenceBits = 12L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final long workerId;
    private final long datacenterId;
    private AtomicLong sequence = new AtomicLong(0);
    private volatile long lastTimestamp = -1L;
    private final ReentrantLock lock = new ReentrantLock();
    // 自动获取机器ID
    public SnowflakeIdGeneratorV2() {
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getWorkerId(maxWorkerId, datacenterId);
    }
    public SnowflakeIdGeneratorV2(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                String.format("Worker ID must be between 0 and %d", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                String.format("Datacenter ID must be between 0 and %d", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    public long nextId() {
        long timestamp = timeGen();
        lock.lock();
        try {
            if (timestamp < lastTimestamp) {
                // 处理时钟回拨
                handleClockBackwards(lastTimestamp - timestamp);
                timestamp = timeGen();
            }
            if (lastTimestamp == timestamp) {
                long currentSequence = sequence.incrementAndGet() & sequenceMask;
                if (currentSequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
                sequence.set(currentSequence);
            } else {
                sequence.set(0L);
            }
            lastTimestamp = timestamp;
        } finally {
            lock.unlock();
        }
        return ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift)
                | sequence.get();
    }
    private void handleClockBackwards(long offset) {
        if (offset > 5) {
            throw new RuntimeException(
                String.format("Clock moved backwards. Refusing for %d milliseconds", offset));
        }
        try {
            Thread.sleep(offset * 10);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Thread interrupted while waiting for clock recovery");
        }
    }
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    private long timeGen() {
        return System.currentTimeMillis();
    }
    // 自动获取数据中心ID
    private 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();
                if (null != mac) {
                    id = ((0x000000FF & (long) mac[mac.length - 1]) 
                         | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                    id = id % (maxDatacenterId + 1);
                }
            }
        } catch (Exception e) {
            id = 1L;
        }
        return id;
    }
    // 自动获取工作机器ID
    private long getWorkerId(long maxWorkerId, long datacenterId) {
        StringBuilder macString = new StringBuilder();
        try {
            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface networkInterface = interfaces.nextElement();
                byte[] mac = networkInterface.getHardwareAddress();
                if (mac != null) {
                    for (byte b : mac) {
                        macString.append(String.format("%02X", b));
                    }
                    break;
                }
            }
        } catch (Exception e) {
            // 使用默认值
        }
        return (macString.toString().hashCode() % (maxWorkerId + 1) + datacenterId) % (maxWorkerId + 1);
    }
    // 解析ID
    public static Map<String, Long> parseId(long id) {
        long sequence = id & 0xFFFL;
        long workerId = (id >> 12) & 0x1FL;
        long datacenterId = (id >> 17) & 0x1FL;
        long timestamp = (id >> 22) + 1609459200000L;
        Map<String, Long> result = new HashMap<>();
        result.put("timestamp", timestamp);
        result.put("datacenterId", datacenterId);
        result.put("workerId", workerId);
        result.put("sequence", sequence);
        return result;
    }
}

使用示例

public class SnowflakeIdDemo {
    public static void main(String[] args) {
        // 方式1:手动指定机器ID
        SnowflakeIdGenerator generator1 = new SnowflakeIdGenerator(1L, 1L);
        long id1 = generator1.nextId();
        System.out.println("Generated ID: " + id1);
        // 方式2:自动获取机器ID
        SnowflakeIdGeneratorV2 generator2 = new SnowflakeIdGeneratorV2();
        long id2 = generator2.nextId();
        System.out.println("Generated ID: " + id2);
        // 解析ID
        Map<String, Long> parsed = SnowflakeIdGeneratorV2.parseId(id2);
        System.out.println("Parsed ID: " + parsed);
        // 批量生成测试
        Set<Long> idSet = new HashSet<>(10000);
        for (int i = 0; i < 10000; i++) {
            long id = generator2.nextId();
            if (!idSet.add(id)) {
                System.out.println("Duplicate ID found: " + id);
            }
        }
        System.out.println("Generated 10000 unique IDs successfully");
    }
}

Spring Boot集成

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
@Configuration
public class SnowflakeConfig {
    @Value("${snowflake.workerId:1}")
    private long workerId;
    @Value("${snowflake.datacenterId:1}")
    private long datacenterId;
    @Bean
    public SnowflakeIdGeneratorV2 snowflakeIdGenerator() {
        return new SnowflakeIdGeneratorV2(workerId, datacenterId);
    }
}
// 使用示例
@Service
public class OrderService {
    @Autowired
    private SnowflakeIdGeneratorV2 snowflakeIdGenerator;
    public Long createOrderId() {
        return snowflakeIdGenerator.nextId();
    }
}

高级特性实现

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class SnowflakeIdPool {
    private final BlockingQueue<Long> idPool;
    private final SnowflakeIdGeneratorV2 generator;
    private volatile boolean running = true;
    public SnowflakeIdPool(int poolSize, int workerId, int datacenterId) {
        this.idPool = new LinkedBlockingQueue<>(poolSize);
        this.generator = new SnowflakeIdGeneratorV2(workerId, datacenterId);
        startIdProducer();
    }
    private void startIdProducer() {
        Thread producer = new Thread(() -> {
            while (running) {
                try {
                    if (idPool.remainingCapacity() > 0) {
                        long id = generator.nextId();
                        idPool.offer(id);
                    }
                    Thread.sleep(1);
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }, "snowflake-id-producer");
        producer.setDaemon(true);
        producer.start();
    }
    public Long getId() {
        try {
            return idPool.poll(100, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return generator.nextId();
        }
    }
    public void shutdown() {
        this.running = false;
    }
}

关键设计要点

  1. 唯一性保证:时间戳+机器ID+序列号组合
  2. 高性能:使用位运算和原子操作
  3. 时钟回拨处理:等待或抛异常
  4. 线程安全:使用synchronized或Lock

配置建议

# application.yml
snowflake:
  workerId: 1
  datacenterId: 1

这个实现适合分布式系统,每秒可生成数百万个唯一ID。

抱歉,评论功能暂时关闭!