All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy