package com.lgq.demo.common.util;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import java.net.*;
/**
* Twitter的雪花算法分布式自增ID
*
* @Author: guanqin_li
* @Date: 2020-12-16 14:58
*/
public class IdGeneratorUtil {
public static Long mac;
public static Long ip;
/**
* 开始时间截
*/
private final static long START_STMP = 1612750939000L;
/**
* 机器id所占的位数
*/
private final static long MACHINE_BIT = 5L;
/**
* 数据标识id所占的位数
*/
private final static long DATACENTER_BIT = 5L;
/**
* 序列在id中占的位数
*/
private final static long SEQUENCE_BIT = 12L;
/**
* 机器ID向左移12位
*/
private final static long MACHINE_LEFT_SHIFT = SEQUENCE_BIT;
/**
* 数据标识id向左移17位(12+5)
*/
private final static long DATACENTER_LEFT_SHIFT = SEQUENCE_BIT + MACHINE_BIT;
/**
* 时间截向左移22位(5+5+12)
*/
private final static long TIMESTMP_LEFT_SHIFT = SEQUENCE_BIT + MACHINE_BIT + DATACENTER_BIT;
/**
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
*/
private final static long SEQUENCE_MASK = ~(-1L << SEQUENCE_BIT);
/**
* 毫秒内序列(0~4095)
*/
private static long sequence = 0L;
/**
* 上次生成ID的时间截
*/
private static long lastTimestamp = -1L;
private IdGeneratorUtil() {
mac = getMac();
ip = getIp();
}
private static class SingleInstance {
private static final IdGeneratorUtil INSTANCE = new IdGeneratorUtil();
}
public static IdGeneratorUtil getInstance() {
return SingleInstance.INSTANCE;
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
*
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
protected static long getNextMill(long lastTimestamp) {
long timestamp = getCurrentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = getCurrentTimeMillis();
}
return timestamp;
}
/**
* 返回以毫秒为单位的当前时间
*
* @return 当前时间(毫秒)
*/
protected static long getCurrentTimeMillis() {
return System.currentTimeMillis();
}
private static Long getMac() {
try {
InetAddress address = InetAddress.getLocalHost();
NetworkInterface net = NetworkInterface.getByInetAddress(address);
byte[] macBytes = net.getHardwareAddress();
int sum = 0;
for (int b : macBytes) {
sum += Math.abs(b);
}
return (long) (sum % 32);
} catch (UnknownHostException | SocketException e) {
e.printStackTrace();
// 如果获取失败,则使用随机数备用
return RandomUtils.nextLong(0, 31);
}
}
private static Long getIp() {
try {
String hostAddress = Inet4Address.getLocalHost().getHostAddress();
int[] codePoints = StringUtils.toCodePoints(hostAddress);
int sum = 0;
for (int b : codePoints) {
sum += b;
}
return (long) (sum % 32);
} catch (UnknownHostException e) {
e.printStackTrace();
// 如果获取失败,则使用随机数备用
return RandomUtils.nextLong(0, 31);
}
}
/**
* 获得下一个ID (该方法是线程安全的)
*
* @return long类型的id
*/
public synchronized long nextLongId() {
long timestamp = getCurrentTimeMillis();
// 如果当前时间小于上一次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) & SEQUENCE_MASK;
// 毫秒内序列溢出
if (sequence == 0) {
// 阻塞到下一个毫秒,获得新的时间戳
timestamp = getNextMill(lastTimestamp);
}
}
// 时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}
// 上次生成ID的时间截
lastTimestamp = timestamp;
// 移位并通过或运算拼到一起组成64位的ID
return ((timestamp - START_STMP) << TIMESTMP_LEFT_SHIFT)
| (mac << DATACENTER_LEFT_SHIFT)
| (ip << MACHINE_LEFT_SHIFT)
| sequence;
}
/**
* 获得下一个ID (该方法是线程安全的)
*
* @return String类型的id
*/
public String nextStringId() {
return Long.toString(nextLongId());
}
}
雪花算法分布式自增ID
最新推荐文章于 2024-03-15 15:57:26 发布