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