使用场景:
//生成出库单号
String snowFlakeID= String.valueOf(snowFlakeIDGenService.getSnowFlakeID());
return DELIVERY_ORDER_NO_PRE + snowFlakeID;
//生成商户订单号
TransfersDto model = new TransfersDto();
String snowFlakeID= String.valueOf(snowFlakeIDGenService.getSnowFlakeID());
model.setPartner_trade_no(snowFlakeID);
//生成活动订单号或者是优惠订单号
CouponClctRec couponClctRec = CouponClctRec.builder()
.withActOrderNo(String.valueOf(snowFlakeIDGenService.getSnowFlakeID()))
//生成防重字符串
String snowFlakeID= String.valueOf(snowFlakeIDGenService.getSnowFlakeID());
cacheService.set(CHECK_FLAG + userId,snowFlakeID, ExpireTime.FIVE_MINUTES)
...
方案一(UUID):
import java.util.UUID;
public class UuidUtil {
public static String getUUid() {
UUID uuid = UUID.randomUUID();
return String.valueOf(uuid).replace("-","");
}
}
方案二(雪花算法):
/**
* 雪花算法ID生成
*/
public class SnowIdWorkerUtil {
// 开始时间截 (2020-01-02)
private final long timeToCut = 1577894400000L;
// 机器ID所占的位数
private final long workerIdBits = 2L;
// 数据标识ID所占的位数
private final long dataCenterIdBits = 8L;
// 支持的最大机器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;
// 生成序列的掩码
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 SnowIdWorkerUtil (long workerId, long dataCenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("workerId 不符合条件");
}
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
throw new IllegalArgumentException("dataCenterId 不符合条件");
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
}
public synchronized String nextIdVar(){
return String.valueOf(nextId());
}
/**
* 线程安全,获得下一个ID
*/
private synchronized long nextId() {
long timestamp = timeGen();
// 如果当前时间小于上一次ID生成的时间戳,抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format(
"时间顺序异常,时间差(上次时间-现在)=%d",
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 - timeToCut) << 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();
}
public static void main(String[] args) {
// 参数在实际业务下需要配置管理
SnowIdWorkerUtil idWorker = new SnowIdWorkerUtil(1, 1);
for (int i = 0; i < 100; i++) {
String id = idWorker.nextIdVar();
System.out.println(id+" "+id.length()+"位");
}
}
}
/**
* 用来生成平台设备出库单号
* 当机器ID占10位,数据中心占0位,初始时间为2012-02-02,可以保证从2020年7月30日一直用到2080年7月30日,且生成的id均为19位正整数
*/
@DubboService(interfaceName = "snowFlakeIDGenService", group = "sys-erp", version = "0.0.1", timeout = 6000)
public class SnowFlakeIDGenServiceImpl implements SnowFlakeIDGenService {
// 下面两个每个5位,加起来就是10位的工作机器id
private long workId; // 工作id
private long dataCenterId; // 数据id
// 序列号id长度
private long sequenceBits = 6L;
// 12位的序列号
private long sequence = (1 << sequenceBits) - 1;
// 初始时间戳
private long twepoch = LocalDateTime.of(2012, 2, 2, 0, 0, 0, 0).toInstant(ZoneOffset.of("+8")).toEpochMilli();
// 长度为5位
private long workerIdBits = 8L;
private long datacenterIdBits = 8L;
// 最大值
// private long maxWorkerId = -1L ^ (-1L << workerIdBits);
// private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 序列号最大值
private long sequenceMask = -1L ^ (-1L << sequenceBits);
// 工作id需要左移的位数,12位
private long workerIdShift = sequenceBits;
// 数据id需要左移位数 12+5=17位
private long datacenterIdShift = sequenceBits + workerIdBits;
// 时间戳需要左移位数 12+5+5=22位
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
// 上次时间戳,初始值为负数
private long lastTimestamp = -1L;
private SnowFlakeIDGenServiceImpl() {
String ipAddress = getIpAddress();
if(Optional.ofNullable(ipAddress).isPresent()){
String[] ipAddressArr = ipAddress.split("\\.");
this.dataCenterId = Long.parseLong(ipAddressArr[2]);
this.workId = Long.parseLong(ipAddressArr[3]);
}else{
this.dataCenterId = 0L;
this.workId = 0L;
}
}
@Override
public long getSnowFlakeID() {
return nextId();
}
@Override
public Long[] getOriginalData(long id) {
SnowFlakeIDGenServiceImpl impl = new SnowFlakeIDGenServiceImpl();
String binaryString = Long.toBinaryString(id);
String str1 = binaryString.substring(binaryString.length()-(int)impl.workerIdShift,binaryString.length());
String str2 = binaryString.substring(binaryString.length()-(int)impl.datacenterIdShift,binaryString.length()-(int)impl.workerIdShift);
String str3 = binaryString.substring(binaryString.length()-(int)impl.timestampLeftShift,binaryString.length()-(int)impl.datacenterIdShift);
String str4 = binaryString.substring(0,binaryString.length()-(int)impl.timestampLeftShift);
return new Long[]{
StringUtils.isEmpty(str4)? 0L: Long.parseLong(str4,2)+impl.twepoch,
StringUtils.isEmpty(str3)? 0L: Long.parseLong(str3,2),
StringUtils.isEmpty(str2)? 0L: Long.parseLong(str2,2),
StringUtils.isEmpty(str1)? 0L: Long.parseLong(str1,2)
};
}
private synchronized long nextId() {
long timestamp = timeGen();
// 获取当前时间戳如果小于上次时间戳,则表示时间戳获取出现异常
if (timestamp < lastTimestamp) {
System.err.printf("clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
// 获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
// 将上次时间戳值刷新
lastTimestamp = timestamp;
/*
* 返回结果: (timestamp - twepoch) << timestampLeftShift) 表示将时间戳减去初始时间戳,再左移相应位数 (datacenterId << datacenterIdShift)
* 表示将数据id左移相应位数 (workerId << workerIdShift) 表示将工作id左移相应位数 | 是按位或运算符,例如:x | y,只有当x,y都为0的时候结果才为0,其它情况结果都为1。
* 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id
*/
return ((timestamp - twepoch) << timestampLeftShift) | (dataCenterId << datacenterIdShift) | (workId << workerIdShift) | sequence;
}
// 获取时间戳,并与上次时间戳比较
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
// 获取系统时间戳
private long timeGen() {
return System.currentTimeMillis();
}
private String getIpAddress() {
try {
Enumeration<InetAddress> inetAddresses = NetworkInterface.getByName("eth0").getInetAddresses();
List<InetAddress> list = Collections.list(inetAddresses).stream().filter(item -> item.getHostAddress().contains(".")).collect(Collectors.toList());
return list.size() == 1 ? list.get(0).getHostAddress() : null;
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
long id = new SnowFlakeIDGenServiceImpl().getSnowFlakeID();
Long[] originalData = new SnowFlakeIDGenServiceImpl().getOriginalData(id);
System.out.println(convertDateToStr(convertTsToDate(String.valueOf(originalData[0])),"yyyy-MM-dd HH:mm:ss")
+ "|" + originalData[1]
+ "|" +originalData[2]
+ "|" + originalData[3]
);
}
//时间戳转Date
private static Date convertTsToDate(String ts){
try {
Instant timestamp = Instant.ofEpochMilli(Long.valueOf(ts));
ZonedDateTime zonedDateTime = timestamp.atZone(ZoneId.of("Asia/Shanghai"));
return Date.from(zonedDateTime.toInstant());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//Date转字符串
private static String convertDateToStr(Date date,String pattrn){
if(date == null){
return StringUtils.EMPTY;
}
Instant instant = date.toInstant();
ZoneId zone = ZoneId.of("Asia/Shanghai");
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattrn);
return localDateTime.format(fmt);
}
}