com.yn.keygen.DefaultKeyGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.yn.keygen.DefaultKeyGenerator.java

Source

package com.yn.keygen;

/*
 * Copyright 1999-2015 dangdang.com.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * </p>
 */

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.util.Calendar;

/**
 * ?.
 *
 * <p>
 * 64bit,???
 * </p>
 *
 * <pre>
 * 1bit   ??
 * 41bits ???2016111
 * 10bits Id
 * 12bits ??
 * </pre>
 *
 * <p>
 * Id?: ??{@code sharding-jdbc.default.key.generator.worker.id}  ??{@code SHARDING_JDBC_DEFAULT_KEY_GENERATOR_WORKER_ID}
 * ,??@{@code DefaultKeyGenerator.setWorkerId}
 * </p>
 *
 * @author yangnan
 */
public final class DefaultKeyGenerator implements KeyGenerator {

    public static final long EPOCH;

    public static final String WORKER_ID_PROPERTY_KEY = "spring-tools.default.key.generator.worker.id";

    public static final String WORKER_ID_ENV_KEY = "SPRING_TOOLS_DEFAULT_KEY_GENERATOR_WORKER_ID";

    private static final long SEQUENCE_BITS = 12L;

    private static final long WORKER_ID_BITS = 10L;

    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 TimeService timeService = new TimeService();

    private static long workerId;

    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();
        initWorkerId();
    }

    private long sequence;

    private long lastTime;

    public static void initWorkerId() {
        String workerId = System.getProperty(WORKER_ID_PROPERTY_KEY);
        if (StringUtils.isNotBlank(workerId)) {
            setWorkerId(Long.valueOf(workerId));
            return;
        }
        workerId = System.getenv(WORKER_ID_ENV_KEY);
        if (StringUtils.isBlank(workerId)) {
            return;
        }
        setWorkerId(Long.valueOf(workerId));
    }

    /**
     * Id.
     *
     * @param workerId Id
     */
    public static void setWorkerId(final long workerId) {
        Assert.isTrue(workerId >= 0L && workerId < WORKER_ID_MAX_VALUE);
        DefaultKeyGenerator.workerId = workerId;
    }

    /**
     * ?Id.
     *
     * @return @{@link Long}Id
     */
    @Override
    public synchronized Number generateKey() {
        long currentMillis = timeService.getCurrentMillis();
        //        Assert.isTrue(lastTime <= currentMillis, String.format("Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastTime, currentMillis));
        if (lastTime == currentMillis) {
            if (0L == (sequence = ++sequence & SEQUENCE_MASK)) {
                currentMillis = waitUntilNextTime(currentMillis);
            }
        } else {
            sequence = 0;
        }
        lastTime = currentMillis;
        return ((currentMillis - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (workerId << WORKER_ID_LEFT_SHIFT_BITS)
                | sequence;
    }

    private long waitUntilNextTime(final long lastTime) {
        long time = timeService.getCurrentMillis();
        while (time <= lastTime) {
            time = timeService.getCurrentMillis();
        }
        return time;
    }
}