leisure.springboot.id.IdOfDay Maven / Gradle / Ivy
The newest version!
package leisure.springboot.id;
import leisure.core.common.FileUtil;
import leisure.core.common.TimeUtil;
import leisure.springboot.core.BeanFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import javax.imageio.IIOException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
//@Component
//@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class IdOfDay {
private Logger logger = LoggerFactory.getLogger(IdOfDay.class);
/**
* 开始时间截 (2018-06-02 00:00:00) 精确到天 占用15位
*/
private final long twepoch = 17683L;
/**
* 支持8个机房 占用3位
*/
private final long datacenterIdBits = 3L;
/**
* 每个机房支持32台机器 占用5位
*/
private final long workerIdBits = 5L;
/**
* 群号在id中占的位数为41
*/
private long sequenceBits = 9L;
/**
* 机器ID向左移41位
*/
private long workerIdShift = sequenceBits;
/**
* 机房ID向左移46位
*/
private long datacenterIdShift = workerIdShift + workerIdBits;
/**
* 时间截向左移49位
*/
private long timestampLeftShift = datacenterIdShift + datacenterIdBits;
/**
* 生成序列的掩码
*/
private long sequenceMask = -1L ^ (-1L << sequenceBits);
/**
* 数据中心ID(0~7)
*/
private long datacenterId = 0;
/**
* 支持的最大数据标识id,结果是31
*/
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/**
* 工作机器ID(0~31)
*/
private long workerId = 0;
/**
* 支持的最大机器id 最大值31
*/
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/**
* 当天序列范围(0~511)
*/
private long sequence = 0L;
/**
* 上次生成ID的时间截
*/
private long lastTimestamp = -1L;
/**
* 当天时间戳
*/
private long timestamp = TimeUtil.getTimeStampMillis() / 1000 / (60 * 60 * 24);
private String rootPath = "/var/cicada/";
private File fileSeq = null;
private final byte[] lock = new byte[1];
private final byte[] perLock = new byte[1];
private IdOfDay() throws Exception {
Environment environment = BeanFactory.getBeanByType(Environment.class);
String strDatacenterId = environment.getProperty("DatacenterId");
String strWorkerId = environment.getProperty("leisure.workid");
String seqDir = environment.getProperty("leisure.id-location");
if (strDatacenterId != null && !strDatacenterId.isEmpty()) {
datacenterId = Long.valueOf(strDatacenterId);
}
if (strWorkerId != null && !strWorkerId.isEmpty()) {
workerId = Long.valueOf(strDatacenterId);
}
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));
}
if (seqDir != null && !seqDir.isEmpty()) {
rootPath = seqDir;
}
recoverySeq();
}
/**
* 获得全局分布式ID
*/
public long getId() {
synchronized (lock) {
long result = generateId();
return result;
}
}
private long generateId() {
if (lastTimestamp == timestamp) {
sequence = sequence + 1;
resetLentgth(sequence);
sequence = sequence & sequenceMask;
timestamp = TimeUtil.getTimeStampMillis() / 1000 / (60 * 60 * 24);
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
long result = ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) | (workerId << workerIdShift) //
| sequence;
return result;
}
/**
* @param value
* @return
*/
private void resetLentgth(long value) {
int length = (int) Math.ceil((Math.log(value + 1) / Math.log(2)));
sequenceBits = length;
if (sequenceBits < 9) {
sequenceBits = 9;
}
workerIdShift = sequenceBits;
datacenterIdShift = workerIdShift + workerIdBits;
timestampLeftShift = datacenterIdShift + datacenterIdBits;
sequenceMask = -1L ^ (-1L << sequenceBits);
}
private long temp = -1L;
/**
* 数据持久化
*
* @throws IOException
* @throws FileNotFoundException
*/
// @Scheduled(cron = "0/20 * * * * *")
private void persistence() {
synchronized (perLock) {
if (temp != sequence && lastTimestamp != -1) {
String data = String.format("%s,%s", lastTimestamp, sequence);
try {
FileUtil.string2file(data, fileSeq);
temp = sequence;
} catch (Exception e) {
String msg = String.format("持久化id 序列出错:", e.getMessage());
logger.error(msg);
}
}
}
}
private void recoverySeq() throws Exception {
logger.info("项目目录:" + rootPath);
if (rootPath != null) {
File dir = new File(rootPath);
if (!dir.exists()) {
dir.mkdirs();
}
rootPath = String.format("%s%s", rootPath, "sequence");
fileSeq = new File(rootPath);
if (!fileSeq.exists()) {
try {
fileSeq.createNewFile();
} catch (Exception e) {
throw new IIOException("创建cicada-id 序列号文件错误:" + e.getStackTrace());
}
} else {
readSeq();
}
}
}
private void readSeq() throws Exception {
try {
String per = FileUtil.file2String(fileSeq);
if (per != null && !per.isEmpty()) {
String[] array = per.split(",");
String lastTimeStr = array[0];
String seqStr = array[1];
if (lastTimeStr != null && !lastTimeStr.isEmpty()) {
lastTimestamp = Long.valueOf(lastTimeStr);
}
if (seqStr != null && !seqStr.isEmpty()) {
sequence = Long.valueOf(seqStr);
sequence = sequence + 2;
}
}
} catch (Exception e) {
throw new Exception("读取cicada-id 序列号文件错误:" + e.getStackTrace());
}
}
}