1, bug现象: 没有经过处理的Snowflake 生成的是64位bit的唯一ID,但由于多数时候我们前台用到js,但是js只支持53位bit的数值。这样就导致了传到前台的64位的丢失精度。

      解决思路:修改SnowFlake 的算法,使它生成 53bit的唯一ID,就可以了,代码如下

package com.wisdombud.product.configure.snowflake;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SnowflakeIdWorker {

    private static final Pattern PATTERN_LONG_ID = Pattern.compile("^([0-9]{15})([0-9a-f]{32})([0-9a-f]{3})$");

    private static final Pattern PATTERN_HOSTNAME = Pattern.compile("^.*\\D+([0-9]+)$");

    private static final long OFFSET = LocalDate.of(2000, 1, 1).atStartOfDay(ZoneId.of("Z")).toEpochSecond();

    private static final long MAX_NEXT = 0b11111_11111111_111L;

    private static final long SHARD_ID = getServerIdAsLong();

    private static long offset = 0;

    private static long lastEpoch = 0;

    public static long getNextId() {
        return nextId(System.currentTimeMillis() / 1000);
    }

    private static synchronized long nextId(long epochSecond) {
        if (epochSecond < lastEpoch) {
            epochSecond = lastEpoch;
        }
        if (lastEpoch != epochSecond) {
            lastEpoch = epochSecond;
            reset();
        }
        offset++;
        long next = offset & MAX_NEXT;
        if (next == 0) {
            return nextId(epochSecond + 1);
        }
        return generateId(epochSecond, next, SHARD_ID);
    }

    private static void reset() {
        offset = 0;
    }

    private static long generateId(long epochSecond, long next, long shardId) {
        return ((epochSecond - OFFSET) << 21) | (next << 5) | shardId;
    }

    private static long getServerIdAsLong() {
        try {
            String hostname = InetAddress.getLocalHost().getHostName();
            Matcher matcher = PATTERN_HOSTNAME.matcher(hostname);
            if (matcher.matches()) {
                long n = Long.parseLong(matcher.group(1));
                if (n >= 0 && n < 8) {
                    return n;
                }
            }
        } catch (UnknownHostException e) {
            
        }
        return 0;
    }
}

参考自: https://my.oschina.net/u/2552286/blog/3115621/print

 

 

 

 

64位的Snowflake 的算法

package com.wisdombud.product.configure.snowflake;

public class SnowflakeIdWorker {

    private final long twepoch            = 1420041600000L;

    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 Long       workerId           = 2L;

    private Long       datacenterId       = 1L;

    private long       sequence           = 0L;

    private long       lastTimestamp      = -1L;

    public SnowflakeIdWorker() {

    }

    public synchronized long nextId() {
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String
                    .format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                            lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;

            if (sequence == 0) {

                timestamp = tilNextMillis(lastTimestamp);
            }
        }

        else {
            sequence = 0L;
        }

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

    public enum SnowflakeInstance {
        INSTANCE();

        private SnowflakeIdWorker singleton;

        SnowflakeInstance() {
            singleton = new SnowflakeIdWorker();
        }

        public SnowflakeIdWorker getInstance() {
            return singleton;
        }
    }
    
    public static Long getNextId() {
        return SnowflakeInstance.INSTANCE.getInstance().nextId();
    }
}