雪花算法生成出库单号

/**
 * 用来生成平台设备出库单号
 * 当机器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);
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值