Java工具類-基於SnowFlake的短地址生成器
阿新 • • 發佈:2019-01-06
Twitter的SnowFlake演算法,使用SnowFlake演算法生成一個整數,然後轉化為62進制變成一個短地址URL
/** * Twitter的SnowFlake演算法,使用SnowFlake演算法生成一個整數,然後轉化為62進制變成一個短地址URL * @author @author beyond https://github.com/beyondfengyu/SnowFlake * @author xuliugen * @date 2018/04/23 */ public class SnowFlakeShortUrl { /** * 起始的時間戳 */ private final static long START_TIMESTAMP = 1480166465631L; /** * 每一部分佔用的位數 */ private final static long SEQUENCE_BIT = 12; //序列號佔用的位數 private final static long MACHINE_BIT = 5; //機器標識佔用的位數 private final static long DATA_CENTER_BIT = 5; //資料中心佔用的位數 /** * 每一部分的最大值 */ private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT); /** * 每一部分向左的位移 */ private final static long MACHINE_LEFT = SEQUENCE_BIT; private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT; private long dataCenterId; //資料中心 private long machineId; //機器標識 private long sequence = 0L; //序列號 private long lastTimeStamp = -1L; //上一次時間戳 /** * 根據指定的資料中心ID和機器標誌ID生成指定的序列號 * @param dataCenterId 資料中心ID * @param machineId 機器標誌ID */ public SnowFlakeShortUrl(long dataCenterId, long machineId) { if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) { throw new IllegalArgumentException("DtaCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0!"); } if (machineId > MAX_MACHINE_NUM || machineId < 0) { throw new IllegalArgumentException("MachineId can't be greater than MAX_MACHINE_NUM or less than 0!"); } this.dataCenterId = dataCenterId; this.machineId = machineId; } /** * 產生下一個ID * @return */ public synchronized long nextId() { long currTimeStamp = getNewTimeStamp(); if (currTimeStamp < lastTimeStamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (currTimeStamp == lastTimeStamp) { //相同毫秒內,序列號自增 sequence = (sequence + 1) & MAX_SEQUENCE; //同一毫秒的序列數已經達到最大 if (sequence == 0L) { currTimeStamp = getNextMill(); } } else { //不同毫秒內,序列號置為0 sequence = 0L; } lastTimeStamp = currTimeStamp; return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //時間戳部分 | dataCenterId << DATA_CENTER_LEFT //資料中心部分 | machineId << MACHINE_LEFT //機器標識部分 | sequence; //序列號部分 } private long getNextMill() { long mill = getNewTimeStamp(); while (mill <= lastTimeStamp) { mill = getNewTimeStamp(); } return mill; } private long getNewTimeStamp() { return System.currentTimeMillis(); } public static void main(String[] args) { SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl(2, 3); for (int i = 0; i < (1 << 4); i++) { //10進位制 Long id = snowFlake.nextId(); //62進位制 String convertedNumStr = NumericConvertUtils.toOtherNumberSystem(id, 62); //10進位制轉化為62進位制 System.out.println("10進位制:" + id + " 62進位制:" + convertedNumStr); //TODO 執行具體的儲存操作,可以存放在Redis等中 //62進位制轉化為10進位制 System.out.println("62進位制:" + convertedNumStr + " 10進位制:" + NumericConvertUtils.toDecimalNumber(convertedNumStr, 62)); System.out.println(); } } }
/** * 進位制轉換工具,最大支援十進位制和62進位制的轉換 * 1、將十進位制的數字轉換為指定進位制的字串; * 2、將其它進位制的數字(字串形式)轉換為十進位制的數字 * @author xuliugen * @date 2018/04/23 */ public class NumericConvertUtils { /** * 在進製表示中的字元集合,0-Z分別用於表示最大為62進位制的符號表示 */ private static final char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; /** * 將十進位制的數字轉換為指定進位制的字串 * @param number 十進位制的數字 * @param seed 指定的進位制 * @return 指定進位制的字串 */ public static String toOtherNumberSystem(long number, int seed) { if (number < 0) { number = ((long) 2 * 0x7fffffff) + number + 2; } char[] buf = new char[32]; int charPos = 32; while ((number / seed) > 0) { buf[--charPos] = digits[(int) (number % seed)]; number /= seed; } buf[--charPos] = digits[(int) (number % seed)]; return new String(buf, charPos, (32 - charPos)); } /** * 將其它進位制的數字(字串形式)轉換為十進位制的數字 * @param number 其它進位制的數字(字串形式) * @param seed 指定的進位制,也就是引數str的原始進位制 * @return 十進位制的數字 */ public static long toDecimalNumber(String number, int seed) { char[] charBuf = number.toCharArray(); if (seed == 10) { return Long.parseLong(number); } long result = 0, base = 1; for (int i = charBuf.length - 1; i >= 0; i--) { int index = 0; for (int j = 0, length = digits.length; j < length; j++) { //找到對應字元的下標,對應的下標才是具體的數值 if (digits[j] == charBuf[i]) { index = j; } } result += index * base; base *= seed; } return result; } public static void main(String[] args) { System.out.println(toOtherNumberSystem(1857568745871168L, 64)); System.out.println(toDecimalNumber("6CnbJfBt0", 64)); System.out.println(); System.out.println(toOtherNumberSystem(185748383552778241L, 64)); System.out.println(toDecimalNumber("ajWiKPh301", 64)); } }