import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import java.util.Calendar; /** * Default distributed primary key generator. * * <p> * Use snowflake algorithm. Length is 64 bit. * </p> * * <pre> * 1bit sign bit. * 41bits timestamp offset from 2016.11.01(ShardingSphere distributed primary key published data) to now. * 10bits worker process id. * 12bits auto increment offset in one mills * </pre> * * <p> * Call @{@code DefaultKeyGenerator.setWorkerId} to set worker id, default value is 0. * </p> * * <p> * Call @{@code DefaultKeyGenerator.setMaxTolerateTimeDifferenceMilliseconds} to set max tolerate time difference milliseconds, default value is 0. * </p> * * @author gaohongtao */ @Slf4j public final class SnowflakeKeyGenerator { public static final long EPOCH; 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 long workerId; private static int maxTolerateTimeDifferenceMilliseconds = 10; 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(); } private byte sequenceOffset; private long sequence; private long lastMilliseconds; /** * Set work process id. * * @param workerId work process id */ public static void setWorkerId(final long workerId) { if (workerId < 0L || workerId >= WORKER_ID_MAX_VALUE) { throw new IllegalArgumentException(); } SnowflakeKeyGenerator.workerId = workerId; } /** * Set max tolerate time difference milliseconds. * * @param maxTolerateTimeDifferenceMilliseconds max tolerate time difference milliseconds */ public static void setMaxTolerateTimeDifferenceMilliseconds(final int maxTolerateTimeDifferenceMilliseconds) { SnowflakeKeyGenerator.maxTolerateTimeDifferenceMilliseconds = maxTolerateTimeDifferenceMilliseconds; } /** * Generate key. * * @return key type is @{@link Long}. */ public synchronized Long generateKey() { long currentMilliseconds = System.currentTimeMillis(); if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) { currentMilliseconds = System.currentTimeMillis(); } if (lastMilliseconds == currentMilliseconds) { if (0L == (sequence = (sequence + 1) & SEQUENCE_MASK)) { currentMilliseconds = waitUntilNextTime(currentMilliseconds); } } else { vibrateSequenceOffset(); sequence = sequenceOffset; } lastMilliseconds = currentMilliseconds; return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (workerId << WORKER_ID_LEFT_SHIFT_BITS) | sequence; } @SneakyThrows private boolean waitTolerateTimeDifferenceIfNeed(final long currentMilliseconds) { if (lastMilliseconds <= currentMilliseconds) { return false; } long timeDifferenceMilliseconds = lastMilliseconds - currentMilliseconds; if (timeDifferenceMilliseconds >= maxTolerateTimeDifferenceMilliseconds) { log.error(String.format("Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds)); //throw new IllegalStateException(String.format("Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds)); } Thread.sleep(timeDifferenceMilliseconds); return true; } private long waitUntilNextTime(final long lastTime) { long result = System.currentTimeMillis(); while (result <= lastTime) { result = System.currentTimeMillis(); } return result; } private void vibrateSequenceOffset() { sequenceOffset = (byte) (~sequenceOffset & 1); } }
spring boot配置文件:java
keyGenerator: snowflake: workerId: 20 #進程ID,每一個實例都要設置不一樣的值,範圍:0-1023 maxTolerateTime: 1500 #最大容忍時鐘回退時間,單位:毫秒
若是mybati使用了plugins插件,能夠繼承進來spring
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableTransactionManagement @Configuration @MapperScan({"com.csair.module.*.mybatis.mapper"}) public class MybatisPlusConfiguration { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } @Bean public SnowflakeKeyGenerator getSnowflakeKeyGenerator(@Value("${keyGenerator.snowflake.workerId}")Integer workerId, @Value("${keyGenerator.snowflake.maxTolerateTime}")Integer maxTolerateTime){ SnowflakeKeyGenerator.setWorkerId(workerId); SnowflakeKeyGenerator.setMaxTolerateTimeDifferenceMilliseconds(maxTolerateTime); return new SnowflakeKeyGenerator(); } }