生成唯一键算法

package com.test;

import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Description:
 * 生成唯一键算法
 *
 * @Author: leo.xiong
 * @CreateDate: 2021/5/14 13:55
 * @Email: [email protected]
 * @Since:
 */
public class GenerateAUniqueKey {
    private static final Logger LOGGER = LoggerFactory.getLogger(GenerateAUniqueKey.class);
    /**
     * 首位
     */
    private static final String FIRST_INDEX = "0";
    /**
     * IP,12位(IP是有可能改变的,所以这里需要考虑什么是服务器唯一,不能改变,MAC地址也是可以改变的,CPU等也是可以刷)
     */
    private static String IP;

    public static final String LOCAL_HOST = "127.0.0.1";

    public static final String COLON = ":";

    private static final int MAX_IP_LENGTH = 15;

    private static final int MIN_IP_LENGTH = 7;

    private static final String BASIC_BUSINESS = "basicBusiness";
    /**
     * 时间回拨最大尝试次数
     */
    private static final int MAX_TRY_TIME = 5;
    /**
     * 最后一次时间
     */
    private static Long lastTime;

    static {
        try {
            Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
            while (netInterfaces.hasMoreElements()) {
                Enumeration<InetAddress> ni = ((NetworkInterface) netInterfaces.nextElement()).getInetAddresses();
                String ip;
                if (ni.hasMoreElements()) {
                    ip = ni.nextElement().getHostAddress();
                    if (StringUtils.isEmpty(ip)) {
                        continue;
                    }
                    if (!LOCAL_HOST.equals(ip) && ip.indexOf(COLON) == -1 && isIP(ip)) {
                        IP = StringUtils.leftPad(ip.replace(".", ""), 12, '0');
                    }
                }
            }

        } catch (SocketException e) {
        }
    }

    private static boolean isIP(String addr) {
        if (addr.length() < MIN_IP_LENGTH || addr.length() > MAX_IP_LENGTH || "".equals(addr)) {
            return false;
        }
        /**
         * 判断IP格式和范围
         */
        String rexp = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}";
        Pattern pat = Pattern.compile(rexp);
        Matcher mat = pat.matcher(addr);
        boolean ipAddress = mat.find();
        return ipAddress;
    }

    /**
     * 业务类型
     */
    private static final Map<String, String> BUSINESS_INFORMATION_MAP = Maps.newHashMap();

    /**
     * 获取业务ID
     * 6位
     *
     * @return
     */
    private static String buildBusinessQueueId(String businessType) {
        String queueId = BUSINESS_INFORMATION_MAP.get(businessType);
        if (queueId == null) {
            synchronized (BUSINESS_INFORMATION_MAP) {
                Integer hashCode = Math.abs(businessType.hashCode());
                if (hashCode > 999999) {
                    queueId = StringUtils.leftPad(String.valueOf(hashCode).substring(0, 6), 6, '0');
                } else {
                    queueId = StringUtils.leftPad(String.valueOf(hashCode), 6, '0');
                }
                BUSINESS_INFORMATION_MAP.put(businessType, queueId);
            }
        }
        return queueId;
    }

    private static final Map<String, String> TIME_SELF_INCREASING_MAP = new HashMap<>();

    /**
     * 8位自增序列号
     *
     * @return
     */
    private synchronized static String selfIncreasing(String businessTypeTime) {
        if (!TIME_SELF_INCREASING_MAP.containsKey(businessTypeTime)) {
            TIME_SELF_INCREASING_MAP.clear();
        }
        String serialNumber = TIME_SELF_INCREASING_MAP.get(businessTypeTime);
        Long value = 0L;
        if (serialNumber != null) {
            value = Long.valueOf(serialNumber) + 1;
            if (value > 99999999L) {
                try {
                    Thread.sleep(1L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }
        serialNumber = StringUtils.leftPad(value.toString(), 8, '0');
        TIME_SELF_INCREASING_MAP.put(businessTypeTime, serialNumber);
        return serialNumber;
    }

    /**
     * 防止时间回拨
     *
     * @return
     */
    private static Long newCurrentTime() throws Exception {
        if (lastTime == null) {
            //todo可以从redis获取最后一次系统时间
            lastTime = System.currentTimeMillis();
        }
        long newCurrentTime = System.currentTimeMillis();
        int tryTime = 0;
        while (newCurrentTime < lastTime) {
            try {
                Thread.sleep(1L);
            } catch (InterruptedException e) {
            }
            newCurrentTime = System.currentTimeMillis();
            if (tryTime > MAX_TRY_TIME) {
                throw new Exception("Time callback check error, the last time is:" + lastTime + "ms the time now is" + newCurrentTime + "ms");
            } else {
                tryTime++;
            }
        }
        lastTime = newCurrentTime;
        return newCurrentTime;
    }

    public static String generateOnlyID() {
        return generateOnlyID(BASIC_BUSINESS);
    }

    /**
     * 生成唯一键
     * 首位+IP(12)+当前时间毫秒(10)+业务ID(6)+自增序列号(8)
     * 1、当同一秒,同一个业务类型的自增序列号大于99999999L,等待1ms,重新生成
     *
     * @param businessType
     * @return
     */
    public static String generateOnlyID(String businessType) {
        Long currentTime = null;
        try {
            currentTime = newCurrentTime();
        } catch (Exception e) {
            LOGGER.warn("businessType:{}", businessType, e);
            return null;
        }
        businessType = Optional.ofNullable(businessType).orElse(BASIC_BUSINESS);
        String selfIncreasingValue = selfIncreasing(businessType + currentTime);
        if (selfIncreasingValue == null) {
            return generateOnlyID(businessType);
        }
        return FIRST_INDEX + currentTime + IP + buildBusinessQueueId(businessType) + selfIncreasingValue;
    }

    public static void main(String[] args) {
        long firstTime = System.currentTimeMillis();
        for (int i = 0; i < 9999999L; i++) {
            System.out.println(generateOnlyID("测试一下"));
        }
        long useTime = System.currentTimeMillis() - firstTime;
        System.out.println("耗时:" + useTime + "ms");
        System.out.println("平均耗时" + (useTime * 1000 / 9999999) + "us");
    }
}

猜你喜欢

转载自blog.csdn.net/xionglangs/article/details/116792366