Java实现雪花算法案例

wen java案例 7

本文目录导读:

Java实现雪花算法案例

  1. 标准雪花算法实现
  2. 分布式环境下的实现(简化版)
  3. 使用示例和最佳实践
  4. 配置文件方式
  5. 扩展:Redis分布式ID生成器
  6. 使用要点

我来为您提供Java实现雪花算法的完整案例:

标准雪花算法实现

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.HashSet;
import java.util.Set;
/**
 * 雪花算法ID生成器
 * 64位long类型:
 * 1位符号位 + 41位时间戳 + 10位机器ID + 12位序列号
 */
public class SnowflakeIdGenerator {
    // 基础常量
    private static final long START_TIMESTAMP = 1700000000000L; // 起始时间戳,可自定义
    // 各部分位数
    private static final long SEQUENCE_BITS = 12L; // 序列号占用的位数
    private static final long MACHINE_ID_BITS = 10L; // 机器ID占用的位数
    // 最大值计算
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS); // 4095
    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS); // 1023
    // 位移量
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS; // 12
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS; // 22
    // 成员变量
    private final long machineId; // 机器ID
    private long sequence = 0L; // 序列号
    private long lastTimestamp = -1L; // 上一次生成ID的时间戳
    /**
     * 构造函数
     * @param machineId 机器ID (0-1023)
     */
    public SnowflakeIdGenerator(long machineId) {
        if (machineId < 0 || machineId > MAX_MACHINE_ID) {
            throw new IllegalArgumentException(
                String.format("机器ID必须在 0 到 %d 之间", MAX_MACHINE_ID)
            );
        }
        this.machineId = machineId;
    }
    /**
     * 生成下一个ID
     */
    public synchronized long nextId() {
        long currentTimestamp = System.currentTimeMillis();
        if (currentTimestamp < lastTimestamp) {
            // 处理时钟回拨问题
            long offset = lastTimestamp - currentTimestamp;
            if (offset <= 5) { // 容忍5ms的时钟回拨
                try {
                    this.wait(offset << 1); // 等待2倍的时间
                    currentTimestamp = System.currentTimeMillis();
                } catch (InterruptedException e) {
                    throw new RuntimeException("等待时钟前移异常", e);
                }
                if (currentTimestamp < lastTimestamp) {
                    throw new RuntimeException("时钟回拨过大,无法生成ID");
                }
            } else {
                throw new RuntimeException("时钟回拨,当前时间戳异常");
            }
        }
        if (currentTimestamp == lastTimestamp) {
            // 同一毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0) {
                // 序列号用完,等待下一毫秒
                currentTimestamp = getNextTimestamp(lastTimestamp);
            }
        } else {
            // 不同毫秒,序列号重置
            sequence = 0L;
        }
        lastTimestamp = currentTimestamp;
        // 生成ID (时间戳左移22位 | 机器ID左移12位 | 序列号)
        return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
                | (machineId << MACHINE_ID_SHIFT)
                | sequence;
    }
    /**
     * 获取下一个毫秒时间戳
     */
    private long getNextTimestamp(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
    /**
     * 解析ID,返回各组成部分
     */
    public IdParts parseId(long id) {
        IdParts parts = new IdParts();
        parts.timestamp = (id >> TIMESTAMP_SHIFT) + START_TIMESTAMP;
        parts.machineId = (id >> MACHINE_ID_SHIFT) & MAX_MACHINE_ID;
        parts.sequence = id & MAX_SEQUENCE;
        return parts;
    }
    /**
     * ID组成部分
     */
    public static class IdParts {
        public long timestamp;
        public long machineId;
        public long sequence;
        @Override
        public String toString() {
            return String.format("时间戳: %d, 机器ID: %d, 序列号: %d", 
                    timestamp, machineId, sequence);
        }
    }
    // 测试方法
    public static void main(String[] args) throws InterruptedException {
        // 单线程测试
        System.out.println("=== 单线程测试 ===");
        SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1);
        for (int i = 0; i < 5; i++) {
            long id = generator.nextId();
            System.out.println("生成的ID: " + id);
            System.out.println("二进制表示: " + Long.toBinaryString(id));
            System.out.println("解析结果: " + generator.parseId(id));
            System.out.println("------------------------");
        }
        // 多线程并发测试
        System.out.println("\n=== 多线程并发测试 ===");
        testConcurrency();
        // 性能测试
        System.out.println("\n=== 性能测试 ===");
        testPerformance();
    }
    /**
     * 并发测试
     */
    private static void testConcurrency() throws InterruptedException {
        final int threadCount = 10;
        final int idCountPerThread = 1000;
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        Set<Long> ids = java.util.Collections.synchronizedSet(new HashSet<>());
        CountDownLatch latch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            final int machineId = i % 1024;
            executorService.submit(() -> {
                SnowflakeIdGenerator generator = new SnowflakeIdGenerator(machineId);
                for (int j = 0; j < idCountPerThread; j++) {
                    ids.add(generator.nextId());
                }
                latch.countDown();
            });
        }
        latch.await();
        executorService.shutdown();
        int totalCount = threadCount * idCountPerThread;
        long startTime = System.currentTimeMillis();
        // 已经在上面的任务中生成,这里只是统计
        System.out.println("生成的ID总数: " + totalCount);
        System.out.println("唯一ID数量: " + ids.size());
        System.out.println("重复ID数量: " + (totalCount - ids.size()));
    }
    /**
     * 性能测试
     */
    private static void testPerformance() {
        int testCount = 1000000; // 100万个
        SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1);
        long startTime = System.nanoTime();
        for (int i = 0; i < testCount; i++) {
            generator.nextId();
        }
        long endTime = System.nanoTime();
        long durationMs = (endTime - startTime) / 1000000;
        System.out.println("生成 " + testCount + " 个ID用时: " + durationMs + "ms");
        System.out.println("每秒可生成: " + (testCount * 1000.0 / durationMs) + " 个ID");
    }
}

分布式环境下的实现(简化版)

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.UUID;
/**
 * 分布式环境雪花算法实现
 * 支持分布式部署,自动获取机器ID
 */
public class DistributedSnowflakeIdGenerator {
    // 配置常量
    private static final long SEQUENCE_BITS = 12L;
    private static final long DATA_CENTER_BITS = 5L;  // 数据中心位数
    private static final long WORKER_BITS = 5L;       // 工作节点位数
    private static final long SEQUENCE_MAX = ~(-1L << SEQUENCE_BITS);
    private static final long DATA_CENTER_MAX = ~(-1L << DATA_CENTER_BITS);
    private static final long WORKER_MAX = ~(-1L << WORKER_BITS);
    private static final long WORKER_SHIFT = SEQUENCE_BITS;
    private static final long DATA_CENTER_SHIFT = SEQUENCE_BITS + WORKER_BITS;
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_BITS + DATA_CENTER_BITS;
    private final long dataCenterId;
    private final long workerId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    /**
     * 构造函数
     * @param dataCenterId 数据中心ID (0-31)
     * @param workerId 工作节点ID (0-31)
     */
    public DistributedSnowflakeIdGenerator(long dataCenterId, long workerId) {
        if (dataCenterId > DATA_CENTER_MAX || dataCenterId < 0) {
            throw new IllegalArgumentException("数据中心ID不能大于" + DATA_CENTER_MAX + "或小于0");
        }
        if (workerId > WORKER_MAX || workerId < 0) {
            throw new IllegalArgumentException("工作节点ID不能大于" + WORKER_MAX + "或小于0");
        }
        this.dataCenterId = dataCenterId;
        this.workerId = workerId;
    }
    /**
     * 生成ID
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                "时钟回拨,拒绝生成ID,回调了%d毫秒", lastTimestamp - timestamp
            ));
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MAX;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }
        lastTimestamp = timestamp;
        return ((timestamp - 1600000000000L) << TIMESTAMP_SHIFT)
            | (dataCenterId << DATA_CENTER_SHIFT)
            | (workerId << WORKER_SHIFT)
            | sequence;
    }
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    private long timeGen() {
        return System.currentTimeMillis();
    }
    /**
     * 根据主机名获取工作节点ID
     */
    private static long getWorkerIdByHostName() {
        try {
            String hostName = InetAddress.getLocalHost().getHostName();
            long hash = hostName.hashCode();
            return Math.abs(hash % 32);
        } catch (UnknownHostException e) {
            return (long) (UUID.randomUUID().getMostSignificantBits() % 32);
        }
    }
    // 使用示例
    public static void main(String[] args) {
        // 创建分布式ID生成器
        DistributedSnowflakeIdGenerator generator = 
            new DistributedSnowflakeIdGenerator(1, getWorkerIdByHostName());
        // 生成并打印几个ID
        for (int i = 0; i < 10; i++) {
            System.out.println(generator.nextId());
        }
    }
}

使用示例和最佳实践

/**
 * 雪花算法使用示例
 */
public class SnowflakeExample {
    // 单例模式
    private static class SingletonHolder {
        private static final SnowflakeIdGenerator INSTANCE = 
            new SnowflakeIdGenerator(1); // 配置对应的机器ID
    }
    /**
     * 获取全局唯一的ID生成器实例
     */
    public static SnowflakeIdGenerator getInstance() {
        return SingletonHolder.INSTANCE;
    }
    public static void main(String[] args) throws InterruptedException {
        // 1. 基本使用
        SnowflakeIdGenerator generator = getInstance();
        Long id = generator.nextId();
        System.out.println("生成的ID: " + id);
        // 2. 批量生成
        for (int i = 0; i < 5; i++) {
            System.out.println("批量生成: " + generator.nextId());
        }
        // 3. 并发环境下使用
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executor.execute(() -> {
                Long orderId = generator.nextId();
                System.out.println(Thread.currentThread().getName() + " 生成订单ID: " + orderId);
            });
        }
        executor.shutdown();
        // 4. 注意事项
        System.out.println("\n=== 使用注意事项 ===");
        System.out.println("1. 雪花算法生成的ID不是严格的递增,但是趋势递增");
        System.out.println("2. 适用场景:分布式系统主键、订单号、消息ID等");
        System.out.println("3. 需要配置好机器ID和数据中心ID");
        System.out.println("4. 支持高并发,理论每秒可生成400万个ID");
        System.out.println("5. 生成的ID是long类型的64位整数");
    }
}

配置文件方式

import java.io.InputStream;
import java.util.Properties;
/**
 * 配置文件方式创建雪花算法组件
 */
public class SnowflakeFactory {
    /**
     * 从配置文件加载配置并创建生成器
     */
    public static SnowflakeIdGenerator createFromConfig(String configFile) throws Exception {
        Properties props = new Properties();
        try (InputStream input = SnowflakeFactory.class.getClassLoader()
                .getResourceAsStream(configFile)) {
            props.load(input);
        }
        long machineId = Long.parseLong(props.getProperty("machine.id", "1"));
        return new SnowflakeIdGenerator(machineId);
    }
}
// application.properties 配置文件示例:
/*
# 雪花算法配置
machine.id=1
# 起始时间戳(可选)
# epoch.timestamp=1700000000000
*/

扩展:Redis分布式ID生成器

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
 * 基于Redis实现分布式唯一ID
 */
public class RedisIdGenerator {
    private static final String ID_KEY = "current:id";
    private final JedisPool jedisPool;
    public RedisIdGenerator(String host, int port) {
        this.jedisPool = new JedisPool(host, port);
    }
    /**
     * 生成ID
     */
    public long nextId() {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.incr(ID_KEY);
        }
    }
    /**
     * 批量生成ID(优化)
     */
    public long[] nextIds(int count) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.incrBy(ID_KEY, count) > 0 ? null : null;
            // 简化处理,实际应根据业务实现
        }
    }
}

使用要点

  1. 机器ID配置:每台机器必须使用唯一的机器ID,通常在0-1023之间
  2. 时钟同步:确保服务器时间同步,避免时钟回拨问题
  3. 线程安全:使用synchronized确保线程安全
  4. 性能优化:单机每秒可生成数百万个ID
  5. 监控告警:监控时钟回拨情况,异常时及时告警

这个实现已经考虑了:

  • 时钟回拨问题
  • 并发安全
  • 分布式部署
  • 性能优化
  • 可扩展性

您可以根据实际业务需求选择合适的实现方式。

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