雪花算法snowflakeIdWorker使用记录
生成唯一的traceId时候是通过uuid进行生成的,想到有没有什么更好的方法,网上搜索了下性能更好的雪花算法。参考:https://zhuanlan.zhihu.com/p/433690272
自己在使用时候主要是机器id的生成以及怎么避免重复,这个主要是通过ip地址以及hostName进行取余获取。代码如下:
package com.gwm.lafeng.util; import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.net.Inet4Address; import java.net.UnknownHostException; /** * @Author:hongtaofan * @Version:1.0 * @Description: * @Date: 2023/9/7 10:14 */ public class SnowflakeIdWorker { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 开始时间截 (2023-01-01) */ private final long twepoch = 1672502400000L; /** * 机器id所占的位数 */ private final long workerIdBits = 5L; /** * 数据标识id所占的位数 */ private final long datacenterIdBits = 5L; /** * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /** * 支持的最大数据标识id,结果是31 */ private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /** * 序列在id中占的位数 */ private final long sequenceBits = 12L; /** * 机器ID向左移12位 */ private final long workerIdShift = sequenceBits; /** * 数据标识id向左移17位(12+5) */ private final long datacenterIdShift = sequenceBits + workerIdBits; /** * 时间截向左移22位(5+5+12) */ private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /** * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ private final long sequenceMask = -1L ^ (-1L << sequenceBits); /** * 工作机器ID(0~31) */ private long workerId; /** * 数据中心ID(0~31) */ private long datacenterId; /** * 毫秒内序列(0~4095) */ private long sequence = 0L; /** * 上次生成ID的时间截 */ private long lastTimestamp = -1L; /** * 构造函数 * @param workerId 工作ID (0~31) * @param datacenterId 数据中心ID (0~31) */ public SnowflakeIdWorker(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("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } try { workerId = getHostId(Inet4Address.getLocalHost().getHostAddress(),31); datacenterId = getHostId(SystemUtils.getHostName(),31); } catch (UnknownHostException e) { logger.error("获取异常",e); workerId = RandomUtils.nextLong(0,31); datacenterId = RandomUtils.nextLong(0,31) ; } logger.info("workerId:" + workerId +"datacenterId:" + datacenterId); this.workerId = workerId; this.datacenterId = datacenterId; } /** * 获得下一个ID (该方法是线程安全的) * @return SnowflakeId */ public synchronized long nextId() { long timestamp = timeGen(); // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 if (timestamp < lastTimestamp) { throw new RuntimeException( String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } // 如果是同一时间生成的,则进行毫秒内序列 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; // 毫秒内序列溢出 if (sequence == 0) { //阻塞到下一个毫秒,获得新的时间戳 timestamp = tilNextMillis(lastTimestamp); } } // 时间戳改变,毫秒内序列重置 else { sequence = 0L; } // 上次生成ID的时间截 lastTimestamp = timestamp; // 移位并通过或运算拼到一起组成64位的ID return ((timestamp - twepoch) << timestampLeftShift) // | (datacenterId << datacenterIdShift) // | (workerId << workerIdShift) // | sequence; } /** * 阻塞到下一个毫秒,直到获得新的时间戳 * @param lastTimestamp 上次生成ID的时间截 * @return 当前时间戳 */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * 返回以毫秒为单位的当前时间 * @return 当前时间(毫秒) */ protected long timeGen() { return System.currentTimeMillis(); } /** * 根据机器ip以及机器host转化为机器workId * @param s * @param max * @return */ public static int getHostId(String s,int max){ byte[] bytes = s.getBytes(); int sums = 0; for(int b: bytes){ sums += b; } return sums % (max + 1); } /*public static void main(String[] args) throws Exception { int hostAddress = getHostId(Inet4Address.getLocalHost().getHostAddress(),31); int hostName = getHostId(SystemUtils.getHostName(),31); System.out.println(hostAddress +" : " + hostName); SnowflakeIdWorker idWorker = new SnowflakeIdWorker(hostAddress, hostName); Long startNow = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { long id = idWorker.nextId(); } Long endTime = System.currentTimeMillis(); System.out.println("耗时:" + (endTime-startNow) + "毫秒"); }*/ }