/**
* 用来生成平台设备出库单号
* 当机器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);
}
}
雪花算法生成出库单号
于 2020-08-13 11:43:54 首次发布