雪花id

package com.csw.shuanfa.utils.id;

import jodd.util.StringPool;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;

/**
 * 在線時間戳轉(zhuǎn)換工具  http://shijianchuo.wiicha.com/
 * 雪花算法能保證69年不重復(fù)
 * twitter的snowflake算法 -- java實現(xiàn)
默認使用是69年
如果要超過生成的會重復(fù)
可以適當(dāng)縮小數(shù)據(jù)中心位DATACENTER_BIT默認時5,時間戳位默認是41孽椰,
當(dāng)為DATACENTER_BIT為3的時候時間戳為是43可以用278年【當(dāng)然對應(yīng)的DATACENTER_BIT最大長度也會變短為7注意】
 *
 *
 *   @Autowired
 *     private SnowFlakeIdUtil snowFlakeIdUtil;
 *
 *  //同一個機器上是單例【相當(dāng)于上了一把鎖一個一個進】昭娩,不同機器上分布式加鎖或者用網(wǎng)卡地址區(qū)分
 *  相同服務(wù)之間最好指定不同的數(shù)據(jù)中心id和機器id
 *  //    snowflake.datacenterId = 3
 * //    snowflake.machineId = 4
 * //    @Value("${snowflake.datacenterId}")
 * //    private long datacenterIdStr;
 * //
 * //    @Value("${snowflake.machineId}")
 * //    private long machineIdStr;
 * //
 * //    @Bean
 * //    public SnowFlakeIdUtil() {
 * //        //通過當(dāng)前物理網(wǎng)卡地址獲取datacenterId
 * //        this.datacenterId = datacenterIdStr;
 * //        //物理網(wǎng)卡地址+jvm進程pi獲取workerId
 * //        this.machineId = machineIdStr;
 * //    }
 *
 */
@Component
public class SnowFlakeIdUtil {
    private Logger logger = LoggerFactory.getLogger(ObjectIdUtil.class);

    /**
     * 起始的時間戳【系統(tǒng)開發(fā)的時候記得更改http://shijianchuo.wiicha.com/】
     */
    private final long START_STMP = 1480166465631L;

    /*
     * 每一部分占用的位數(shù)
     */
    /**
     * 序列號占用的位數(shù)
     */
    private final long SEQUENCE_BIT = 12;
    /**
     * 機器標(biāo)識占用的位數(shù)
     */
    private final long MACHINE_BIT = 5;
 /**
     * 數(shù)據(jù)中心占用的位數(shù) 數(shù)據(jù)中心占用的位數(shù)默認是5位建議用4位【時間戳就會變?yōu)?2位】,可以使用139年】
     */
    private final long DATACENTER_BIT = 4;

    /**
     * 每一部分的最大值
     */
    private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
    /**
     * 支持的最大機器id弄屡,結(jié)果是31(這個移位算法可以很快的計算出幾位二進制數(shù)所能表示的最大十進制數(shù))
     */
    private final long maxMachineId = -1L ^ (-1L << MACHINE_BIT);

    /**
     * 支持的最大數(shù)據(jù)標(biāo)識id题禀,結(jié)果是31
     */
    private final long maxDatacenterId = -1L ^ (-1L << DATACENTER_BIT);

    /**
     * 每一部分向左的位移
     */
    private final long MACHINE_LEFT = SEQUENCE_BIT;
    private final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
    /**
     * 數(shù)據(jù)中心
     */
    private final long datacenterId;
    /**
     * //機器標(biāo)識
     */
    private final long machineId;
    /**
     * //序列號
     */
    private long sequence = 0L;
    /**
     * 上一次時間戳
     */
    private long lastStmp = -1L;

    public SnowFlakeIdUtil(long datacenterId, long machineId) {
        if (datacenterId <= maxDatacenterId && datacenterId >= 0L) {
            if (machineId <= maxMachineId && machineId >= 0L) {
                this.datacenterId = datacenterId;
                this.machineId = machineId;
            } else {
                throw new IllegalArgumentException(StrUtil.format("datacenter Id can't be greater than {} or less than 0", new Object[]{maxMachineId}));
            }
        } else {
            throw new IllegalArgumentException(StrUtil.format("worker Id can't be greater than {} or less than 0", new Object[]{maxDatacenterId}));
        }

    }

    public SnowFlakeIdUtil() {
        //通過當(dāng)前物理網(wǎng)卡地址獲取datacenterId
        this.datacenterId = getDatacenterId(maxDatacenterId);
        //物理網(wǎng)卡地址+jvm進程pi獲取workerId
        this.machineId = getMaxMachineId(datacenterId, maxMachineId);
    }


    /**
     * 產(chǎn)生下一個ID
     */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currStmp == lastStmp) {
            //相同毫秒內(nèi),序列號自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列數(shù)已經(jīng)達到最大
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //不同毫秒內(nèi)膀捷,序列號置為0
            sequence = 0L;
        }

        lastStmp = currStmp;
        /*
         * //時間戳部分
         * //數(shù)據(jù)中心部分
         * //機器標(biāo)識部分
         * //序列號部分
         */
        return (currStmp - START_STMP) << TIMESTMP_LEFT
                | datacenterId << DATACENTER_LEFT
                | machineId << MACHINE_LEFT
                | sequence;
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }

    protected long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            //獲取本機(或者服務(wù)器ip地址)
            //DESKTOP-123SDAD/192.168.1.87
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            //一般不是null會進入else
            if (network == null) {
                id = 1L;
            } else {
                //獲取物理網(wǎng)卡地址
                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;
    }

    /**
     * 獲取 maxWorkerId
     */
    protected long getMaxMachineId(long datacenterId, long maxMachineId) {
        StringBuilder mpid = new StringBuilder();
        mpid.append(datacenterId);
        //獲取jvm進程信息
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (StringUtils.isNotBlank(name)) {
            /*
             * 獲取進程PID
             */
            mpid.append(name.split(StringPool.AT)[0]);
        }
        /*
         * MAC + PID 的 hashcode 獲取16個低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxMachineId + 1);
    }
}

轉(zhuǎn)62進制

//cn.hutool.core.util

private static final char[] BASE_62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();

 public static String SnowflakeIdtoBase62Id() {
        Long snowflakeId = IdUtil.getSnowflake().nextId();
        return longtoBase62Id(snowflakeId);
    }

    public static String longtoBase62Id(Long snowflakeId) {
        StringBuilder base62 = new StringBuilder();
        do {
            int remainder = (int) (snowflakeId % 62);
            base62.insert(0, BASE_62_CHARS[remainder]);
            snowflakeId = snowflakeId / 62;
        } while (snowflakeId > 0);

        return base62.toString();
    }

轉(zhuǎn)36進制

//cn.hutool.core.util

public static String getSnowflake_36() {
        return Long.toUnsignedString(IdUtil.getSnowflake().nextId(), 36).toUpperCase();
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迈嘹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子全庸,更是在濱河造成了極大的恐慌秀仲,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壶笼,死亡現(xiàn)場離奇詭異神僵,居然都是意外死亡,警方通過查閱死者的電腦和手機覆劈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門保礼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人责语,你說我怎么就攤上這事炮障。” “怎么了坤候?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵胁赢,是天一觀的道長。 經(jīng)常有香客問我白筹,道長智末,這世上最難降的妖魔是什么谅摄? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮系馆,結(jié)果婚禮上送漠,老公的妹妹穿的比我還像新娘。我一直安慰自己由蘑,他們只是感情好螺男,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纵穿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奢人。 梳的紋絲不亂的頭發(fā)上谓媒,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音何乎,去河邊找鬼句惯。 笑死,一個胖子當(dāng)著我的面吹牛支救,可吹牛的內(nèi)容都是我干的抢野。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼各墨,長吁一口氣:“原來是場噩夢啊……” “哼指孤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起贬堵,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤恃轩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后黎做,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叉跛,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年蒸殿,在試婚紗的時候發(fā)現(xiàn)自己被綠了筷厘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宏所,死狀恐怖酥艳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情楣铁,我是刑警寧澤玖雁,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站盖腕,受9級特大地震影響赫冬,放射性物質(zhì)發(fā)生泄漏浓镜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一劲厌、第九天 我趴在偏房一處隱蔽的房頂上張望膛薛。 院中可真熱鬧,春花似錦补鼻、人聲如沸哄啄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咨跌。三九已至,卻和暖如春硼婿,著一層夾襖步出監(jiān)牢的瞬間锌半,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工寇漫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留刊殉,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓州胳,卻偏偏與公主長得像记焊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子栓撞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容