lau572
11 months ago
7 changed files with 266 additions and 4 deletions
@ -0,0 +1,83 @@ |
|||
/* |
|||
* |
|||
* * Copyright 2020 http://www.hswebframework.org
|
|||
* * |
|||
* * 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. |
|||
* |
|||
*/ |
|||
|
|||
package com.ruoyi.common.utils; |
|||
|
|||
|
|||
import java.math.BigInteger; |
|||
import java.security.MessageDigest; |
|||
import java.security.NoSuchAlgorithmException; |
|||
|
|||
/** |
|||
* ID生成器,用于生成ID |
|||
* |
|||
* @author zhouhao |
|||
* @since 3.0 |
|||
*/ |
|||
@FunctionalInterface |
|||
public interface IDGenerator<T> { |
|||
T generate(); |
|||
|
|||
/** |
|||
* 空ID生成器 |
|||
*/ |
|||
IDGenerator<?> NULL = () -> null; |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
static <T> IDGenerator<T> getNullGenerator() { |
|||
return (IDGenerator) NULL; |
|||
} |
|||
|
|||
/** |
|||
* 使用UUID生成id |
|||
*/ |
|||
IDGenerator<String> UUID = () -> java.util.UUID.randomUUID().toString(); |
|||
|
|||
/** |
|||
* 随机字符 |
|||
*/ |
|||
IDGenerator<String> RANDOM = RandomUtil::randomChar; |
|||
|
|||
/** |
|||
* md5(uuid()+random()) |
|||
*/ |
|||
IDGenerator<String> MD5 = () -> { |
|||
try { |
|||
MessageDigest md = MessageDigest.getInstance("MD5"); |
|||
md.update(UUID.generate().concat(RandomUtil.randomChar()).getBytes()); |
|||
return new BigInteger(1, md.digest()).toString(16); |
|||
} catch (NoSuchAlgorithmException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* 雪花算法 |
|||
*/ |
|||
IDGenerator<Long> SNOW_FLAKE = SnowflakeIdGenerator.getInstance()::nextId; |
|||
|
|||
/** |
|||
* 雪花算法转String |
|||
*/ |
|||
IDGenerator<String> SNOW_FLAKE_STRING = () -> String.valueOf(SNOW_FLAKE.generate()); |
|||
|
|||
/** |
|||
* 雪花算法的16进制 |
|||
*/ |
|||
IDGenerator<String> SNOW_FLAKE_HEX = () -> Long.toHexString(SNOW_FLAKE.generate()); |
|||
} |
@ -0,0 +1,46 @@ |
|||
package com.ruoyi.common.utils; |
|||
|
|||
import java.util.Random; |
|||
|
|||
|
|||
/** |
|||
* 随机数工具,用于产生随机数,随机密码等 |
|||
*/ |
|||
public class RandomUtil { |
|||
private static final Random random = new Random(); |
|||
|
|||
public static Random getRandom() { |
|||
return random; |
|||
} |
|||
|
|||
private static char[] chars = { |
|||
'a', 'b', 'c', 'd', 'e', 'f', 'g', |
|||
'h', 'i', 'j', 'k', 'l', 'm', 'n', |
|||
'o', 'p', 'q', 'r', 's', 't', 'u', |
|||
'v', 'w', 'x', 'y', 'z', |
|||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
|||
'A', 'B', 'C', 'D', 'E', 'F', 'G', |
|||
'H', 'I', 'J', 'K', 'L', 'M', 'N', |
|||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
|||
'V', 'W', 'X', 'Y', 'Z' |
|||
}; |
|||
|
|||
/** |
|||
* 随机生成由0-9a-zA-Z组合而成的字符串 |
|||
* |
|||
* @param len 字符串长度 |
|||
* @return 生成结果 |
|||
*/ |
|||
public static String randomChar(int len) { |
|||
StringBuilder sb = new StringBuilder(); |
|||
for (int i = 0; i < len; i++) { |
|||
sb.append(chars[random.nextInt(chars.length)]); |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
public static String randomChar() { |
|||
return randomChar(8); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,90 @@ |
|||
package com.ruoyi.common.utils; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
|
|||
import java.util.*; |
|||
|
|||
@Slf4j |
|||
public class SnowflakeIdGenerator { |
|||
|
|||
private long workerId; |
|||
private long dataCenterId; |
|||
private long sequence = 0L; |
|||
|
|||
private long twepoch = 1288834974657L; |
|||
|
|||
private long workerIdBits = 5L; |
|||
private long datacenterIdBits = 5L; |
|||
private long maxWorkerId = -1L ^ (-1L << workerIdBits); |
|||
private long maxDataCenterId = -1L ^ (-1L << datacenterIdBits); |
|||
private long sequenceBits = 12L; |
|||
|
|||
private long workerIdShift = sequenceBits; |
|||
private long datacenterIdShift = sequenceBits + workerIdBits; |
|||
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; |
|||
private long sequenceMask = -1L ^ (-1L << sequenceBits); |
|||
|
|||
private long lastTimestamp = -1L; |
|||
|
|||
private static final SnowflakeIdGenerator generator; |
|||
|
|||
static { |
|||
Random random = new Random(); |
|||
long workerId = Long.getLong("id-worker", random.nextInt(31)); |
|||
long dataCenterId = Long.getLong("id-datacenter", random.nextInt(31)); |
|||
generator = new SnowflakeIdGenerator(workerId, dataCenterId); |
|||
} |
|||
|
|||
public static SnowflakeIdGenerator getInstance() { |
|||
return generator; |
|||
} |
|||
|
|||
public SnowflakeIdGenerator(long workerId, long dataCenterId) { |
|||
// sanity check for workerId
|
|||
if (workerId > maxWorkerId || workerId < 0) { |
|||
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); |
|||
} |
|||
if (dataCenterId > maxDataCenterId || dataCenterId < 0) { |
|||
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDataCenterId)); |
|||
} |
|||
this.workerId = workerId; |
|||
this.dataCenterId = dataCenterId; |
|||
log.info("worker starting. timestamp left shift {}, datacenter id bits {}, worker id bits {}, sequence bits {}, workerid {}", timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId); |
|||
} |
|||
|
|||
public synchronized long nextId() { |
|||
long timestamp = timeGen(); |
|||
|
|||
if (timestamp < lastTimestamp) { |
|||
log.error("clock is moving backwards. Rejecting requests until {}.", lastTimestamp); |
|||
throw new UnsupportedOperationException(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(); |
|||
} |
|||
|
|||
} |
Loading…
Reference in new issue