11 changed files with 259 additions and 25 deletions
			
			
		@ -0,0 +1,214 @@ | 
				
			|||
/* | 
				
			|||
 * Copyright (c) 2011-2021, baomidou (jobob@qq.com). | 
				
			|||
 * | 
				
			|||
 * Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			|||
 * you may not use this file except in compliance with the License. | 
				
			|||
 * You may obtain a copy of the License at | 
				
			|||
 * | 
				
			|||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			|||
 * | 
				
			|||
 * Unless required by applicable law or agreed to in writing, software | 
				
			|||
 * distributed under the License is distributed on an "AS IS" BASIS, | 
				
			|||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			|||
 * See the License for the specific language governing permissions and | 
				
			|||
 * limitations under the License. | 
				
			|||
 */ | 
				
			|||
package com.epmet.util; | 
				
			|||
 | 
				
			|||
import com.alibaba.fastjson.JSON; | 
				
			|||
import com.baomidou.mybatisplus.core.toolkit.Assert; | 
				
			|||
import com.baomidou.mybatisplus.core.toolkit.StringPool; | 
				
			|||
import com.baomidou.mybatisplus.core.toolkit.StringUtils; | 
				
			|||
import com.baomidou.mybatisplus.core.toolkit.SystemClock; | 
				
			|||
import com.epmet.commons.tools.utils.HttpClientManager; | 
				
			|||
import lombok.extern.slf4j.Slf4j; | 
				
			|||
import org.apache.ibatis.logging.Log; | 
				
			|||
import org.apache.ibatis.logging.LogFactory; | 
				
			|||
 | 
				
			|||
import java.lang.management.ManagementFactory; | 
				
			|||
import java.net.InetAddress; | 
				
			|||
import java.net.NetworkInterface; | 
				
			|||
import java.util.concurrent.ThreadLocalRandom; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * 分布式高效有序 ID 生产黑科技(sequence) | 
				
			|||
 * | 
				
			|||
 * <p>优化开源项目:https://gitee.com/yu120/sequence</p>
 | 
				
			|||
 * | 
				
			|||
 * @author hubin | 
				
			|||
 * @since 2016-08-18 | 
				
			|||
 */ | 
				
			|||
@Slf4j | 
				
			|||
public class MySequence { | 
				
			|||
 | 
				
			|||
    private static final Log logger = LogFactory.getLog(MySequence.class); | 
				
			|||
    /** | 
				
			|||
     * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) | 
				
			|||
     */ | 
				
			|||
    private final long twepoch = 1288834974657L; | 
				
			|||
    /** | 
				
			|||
     * 机器标识位数 | 
				
			|||
     */ | 
				
			|||
    private final long workerIdBits = 5L; | 
				
			|||
    private final long datacenterIdBits = 5L; | 
				
			|||
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits); | 
				
			|||
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); | 
				
			|||
    /** | 
				
			|||
     * 毫秒内自增位 | 
				
			|||
     */ | 
				
			|||
    private final long sequenceBits = 12L; | 
				
			|||
    private final long workerIdShift = sequenceBits; | 
				
			|||
    private final long datacenterIdShift = sequenceBits + workerIdBits; | 
				
			|||
    /** | 
				
			|||
     * 时间戳左移动位 | 
				
			|||
     */ | 
				
			|||
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; | 
				
			|||
    private final long sequenceMask = -1L ^ (-1L << sequenceBits); | 
				
			|||
 | 
				
			|||
    private final long workerId; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 数据标识 ID 部分 | 
				
			|||
     */ | 
				
			|||
    private final long datacenterId; | 
				
			|||
    /** | 
				
			|||
     * 并发控制 | 
				
			|||
     */ | 
				
			|||
    private long sequence = 0L; | 
				
			|||
    /** | 
				
			|||
     * 上次生产 ID 时间戳 | 
				
			|||
     */ | 
				
			|||
    private long lastTimestamp = -1L; | 
				
			|||
 | 
				
			|||
    public MySequence() { | 
				
			|||
        this.datacenterId = getDatacenterId(maxDatacenterId); | 
				
			|||
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); | 
				
			|||
        String msg = "MySequence datacenterId:" + this.datacenterId + ";workerId:" + this.workerId; | 
				
			|||
        log.info(msg); | 
				
			|||
        HttpClientManager.getInstance().sendAlarmMsg(msg); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 有参构造器 | 
				
			|||
     * | 
				
			|||
     * @param workerId     工作机器 ID | 
				
			|||
     * @param datacenterId 序列号 | 
				
			|||
     */ | 
				
			|||
    public MySequence(long workerId, long datacenterId) { | 
				
			|||
        Assert.isFalse(workerId > maxWorkerId || workerId < 0, | 
				
			|||
                String.format("MySequence worker Id can't be greater than %d or less than 0", maxWorkerId)); | 
				
			|||
        Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0, | 
				
			|||
                String.format("MySequence datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); | 
				
			|||
        this.workerId = workerId; | 
				
			|||
        this.datacenterId = datacenterId; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 获取 maxWorkerId | 
				
			|||
     */ | 
				
			|||
    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { | 
				
			|||
        StringBuilder mpid = new StringBuilder(); | 
				
			|||
        mpid.append(datacenterId); | 
				
			|||
        String name = ManagementFactory.getRuntimeMXBean().getName(); | 
				
			|||
        String msg = "MySequence getMaxWorkerId name:" + name; | 
				
			|||
        log.info(msg); | 
				
			|||
        HttpClientManager.getInstance().sendAlarmMsg(msg); | 
				
			|||
        if (StringUtils.isNotBlank(name)) { | 
				
			|||
            /* | 
				
			|||
             * GET jvmPid | 
				
			|||
             */ | 
				
			|||
            mpid.append(name.split(StringPool.AT)[0]); | 
				
			|||
        } | 
				
			|||
        /* | 
				
			|||
         * MAC + PID 的 hashcode 获取16个低位 | 
				
			|||
         */ | 
				
			|||
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 数据标识id部分 | 
				
			|||
     */ | 
				
			|||
    protected static long getDatacenterId(long maxDatacenterId) { | 
				
			|||
        long id = 0L; | 
				
			|||
        try { | 
				
			|||
            InetAddress ip = InetAddress.getLocalHost(); | 
				
			|||
            NetworkInterface network = NetworkInterface.getByInetAddress(ip); | 
				
			|||
            String msg = "MySequence ip:" + JSON.toJSONString(ip) + ";network: " + JSON.toJSONString(network); | 
				
			|||
            log.info(msg); | 
				
			|||
            HttpClientManager.getInstance().sendAlarmMsg(msg); | 
				
			|||
            if (network == null) { | 
				
			|||
                id = 1L; | 
				
			|||
                log.info("MySequen cenetwork ==null "); | 
				
			|||
            } else { | 
				
			|||
                byte[] mac = network.getHardwareAddress(); | 
				
			|||
                if (null != mac) { | 
				
			|||
                    id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6; | 
				
			|||
                    id = id % (maxDatacenterId + 1); | 
				
			|||
                } | 
				
			|||
            } | 
				
			|||
        } catch (Exception e) { | 
				
			|||
            logger.warn(" getDatacenterId: " + e.getMessage()); | 
				
			|||
        } | 
				
			|||
        return id; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 获取下一个 ID | 
				
			|||
     * | 
				
			|||
     * @return 下一个 ID | 
				
			|||
     */ | 
				
			|||
    public synchronized long nextId() { | 
				
			|||
        long timestamp = timeGen(); | 
				
			|||
        //闰秒
 | 
				
			|||
        if (timestamp < lastTimestamp) { | 
				
			|||
            long offset = lastTimestamp - timestamp; | 
				
			|||
            if (offset <= 5) { | 
				
			|||
                try { | 
				
			|||
                    wait(offset << 1); | 
				
			|||
                    timestamp = timeGen(); | 
				
			|||
                    if (timestamp < lastTimestamp) { | 
				
			|||
                        throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset)); | 
				
			|||
                    } | 
				
			|||
                } catch (Exception e) { | 
				
			|||
                    throw new RuntimeException(e); | 
				
			|||
                } | 
				
			|||
            } else { | 
				
			|||
                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset)); | 
				
			|||
            } | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        if (lastTimestamp == timestamp) { | 
				
			|||
            // 相同毫秒内,序列号自增
 | 
				
			|||
            sequence = (sequence + 1) & sequenceMask; | 
				
			|||
            if (sequence == 0) { | 
				
			|||
                // 同一毫秒的序列数已经达到最大
 | 
				
			|||
                timestamp = tilNextMillis(lastTimestamp); | 
				
			|||
            } | 
				
			|||
        } else { | 
				
			|||
            // 不同毫秒内,序列号置为 1 - 3 随机数
 | 
				
			|||
            sequence = ThreadLocalRandom.current().nextLong(1, 3); | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        lastTimestamp = timestamp; | 
				
			|||
 | 
				
			|||
        // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分
 | 
				
			|||
        return ((timestamp - twepoch) << timestampLeftShift) | 
				
			|||
                | (datacenterId << datacenterIdShift) | 
				
			|||
                | (workerId << workerIdShift) | 
				
			|||
                | sequence; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    protected long tilNextMillis(long lastTimestamp) { | 
				
			|||
        long timestamp = timeGen(); | 
				
			|||
        while (timestamp <= lastTimestamp) { | 
				
			|||
            timestamp = timeGen(); | 
				
			|||
        } | 
				
			|||
        return timestamp; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    protected long timeGen() { | 
				
			|||
        return SystemClock.now(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
					Loading…
					
					
				
		Reference in new issue