分布式ID生成器,snowFlake算法在sharding-jdbc中的实现

public final class SnowflakeShardingKeyGenerator implements ShardingKeyGenerator {
    
    public static final long EPOCH;
    
    private static final long SEQUENCE_BITS = 12L;
    
    private static final long WORKER_ID_BITS = 10L;
    //mask的作用就是将sequence所代表的最后12位全部置为1,方便后面做位于运算
    private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;
    
    private static final long WORKER_ID_LEFT_SHIFT_BITS = SEQUENCE_BITS;
    
    private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS;
    
    private static final long WORKER_ID_MAX_VALUE = 1L << WORKER_ID_BITS;
    
    private static final long WORKER_ID = 0;
    
    private static final int MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS = 10;
    
    @Setter
    private static TimeService timeService = new TimeService();
    
    @Getter
    @Setter
    private Properties properties = new Properties();
    
    private byte sequenceOffset;
    /**
     * 毫秒内自增序列(0~4095) 既最后的12个bit位,2^12-1
     */
    private long sequence;
    
    private long lastMilliseconds;
    
    static {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2016, Calendar.NOVEMBER, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        EPOCH = calendar.getTimeInMillis();
    }
    
    @Override
    public String getType() {
        return "SNOWFLAKE";
    }
    
    @Override
    public synchronized Comparable generateKey() {

        long currentMilliseconds = timeService.getCurrentMillis();
        //处理时间回溯问题
        if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) {
            currentMilliseconds = timeService.getCurrentMillis();
        }
        //如果是在同一毫秒中调用此方法,
        if (lastMilliseconds == currentMilliseconds) {
            //这里是一个位运算,因为sequence最多只允许占用最低的12位,一旦+1的步骤达到4096,
            //达到了13位,既2^13,通过和SEQUENCE_MASK做位与运算,重置为0
            if (0L == (sequence = (sequence + 1) & SEQUENCE_MASK)) {
                //一旦重置为0后,说明这一毫秒能生成的key已达到上限,则进入循环等待下一个毫秒。
                // 因为仅仅是一个毫秒的差距,所以没有使用sleep,直接while循环节省cpu调用
                currentMilliseconds = waitUntilNextTime(currentMilliseconds);
            }
        } else {
            //如果不是在同一毫秒中并发调用此方法
            vibrateSequenceOffset();
            sequence = sequenceOffset;
        }
        lastMilliseconds = currentMilliseconds;
        /**
         * (currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) 时间毫秒数往右移动22位,避开workId和sequence,放到高位的41位
         * (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) 将workId左移到随后的10位上
         * 最后是sequence占据低位的12位
         *
         * 最后用或运算将三个部分组合到一个long中,返回最终的结果
         */
        return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | sequence;
    }

    /**
     * 如果当前时间没有回溯过,直接返回false,不等待
     * 如果当前时间回溯过,如果在允许的时间范围内,就sleep,返回true,表示等待过
     *                  如果超过了允许的时间范围,直接抛出异常。
     * @param currentMilliseconds
     * @return
     */
    @SneakyThrows
    private boolean waitTolerateTimeDifferenceIfNeed(final long currentMilliseconds) {
        if (lastMilliseconds <= currentMilliseconds) {
            return false;
        }
        //回溯的时间毫秒数
        long timeDifferenceMilliseconds = lastMilliseconds - currentMilliseconds;

        Preconditions.checkState(timeDifferenceMilliseconds < getMaxTolerateTimeDifferenceMilliseconds(), 
                "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds);
        Thread.sleep(timeDifferenceMilliseconds);
        return true;
    }
    
    private long getWorkerId() {
        long result = Long.valueOf(properties.getProperty("worker.id", String.valueOf(WORKER_ID)));
        Preconditions.checkArgument(result >= 0L && result < WORKER_ID_MAX_VALUE);
        return result;
    }
    
    private int getMaxTolerateTimeDifferenceMilliseconds() {
        return Integer.valueOf(properties.getProperty("max.tolerate.time.difference.milliseconds", String.valueOf(MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS)));
    }
    
    private long waitUntilNextTime(final long lastTime) {
        long result = timeService.getCurrentMillis();
        while (result <= lastTime) {
            result = timeService.getCurrentMillis();
        }
        return result;
    }

    /**
     * byte的初始值0000 0000
     * ~取反后就是-1,二进制1111 1111
     * 和1做位于运算,0000 0001,可以发现,甭管你是多少,最后都只会留下最低位,1或者0
     * sequenceOffset最开始是0,结果就是1
     * sequenceOffset变成1后,一通操作后结果就是0
     *
     * 所以这个sequenceOffset每次调用循环变成0,1.
     * 这个原理是为了让在分布式环境中低QPS的时候,末尾不总是0,就不会全是偶数。造成切片堆积
     * https://www.jianshu.com/p/2428ac820055
     */
    private void vibrateSequenceOffset() {
        sequenceOffset = (byte) (~sequenceOffset & 1);
    }
}

你可能感兴趣的:(分布式ID生成器,snowFlake算法在sharding-jdbc中的实现)