add getWorkerId stc

This commit is contained in:
Looly 2021-06-28 20:49:39 +08:00
parent 8e82919464
commit 16910ed709
7 changed files with 206 additions and 58 deletions

View File

@ -3,7 +3,7 @@
-------------------------------------------------------------------------------------------------------------
# 5.7.3 (2021-06-26)
# 5.7.3 (2021-06-28)
### 🐣新特性
* 【core 】 增加Convert.toSet方法issue#I3XFG2@Gitee
@ -14,6 +14,7 @@
* 【core 】 增加可自定义日期格式GlobalCustomFormat
* 【jwt 】 JWT修改默认有序并规定payload日期格式为秒数
* 【json 】 增加JSONWriter
* 【core 】 IdUtil增加getWorkerId和getDataCenterIdissueI3Y5NI@Gitee
### 🐞Bug修复
* 【json 】 修复XML转义字符的问题issue#I3XH09@Gitee

View File

@ -1,16 +1,16 @@
package cn.hutool.core.lang;
import java.lang.management.ManagementFactory;
import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.StrUtil;
import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;
/**
* MongoDB ID生成策略实现<br>
* ObjectId由以下几部分组成
@ -156,14 +156,7 @@ public class ObjectId {
// 进程ID初始化
int processId;
try {
// 获取进程ID
final String processName =ManagementFactory.getRuntimeMXBean().getName();
final int atIndex = processName.indexOf('@');
if (atIndex > 0) {
processId = Integer.parseInt(processName.substring(0, atIndex));
} else {
processId = processName.hashCode();
}
processId = RuntimeUtil.getPid();
} catch (Throwable t) {
processId = RandomUtil.randomInt();
}

View File

@ -1,6 +1,7 @@
package cn.hutool.core.lang;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import java.io.Serializable;
@ -14,6 +15,7 @@ import java.util.Date;
* snowflake的结构如下(每部分用-分开):<br>
*
* <pre>
* 符号位1bit- 时间戳相对值41bit- 数据中心标志5bit- 机器标志5bit- 递增序号12bit
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* </pre>
* <p>
@ -23,7 +25,8 @@ import java.util.Date;
* <p>
* 并且可以通过生成的id反推出生成时间,datacenterId和workerId
* <p>
* 参考http://www.cnblogs.com/relucent/p/4955340.html
* 参考http://www.cnblogs.com/relucent/p/4955340.html<br>
* 关于长度是18还是19的问题见https://blog.csdn.net/unifirst/article/details/80408050
*
* @author Looly
* @since 3.0.1
@ -31,33 +34,63 @@ import java.util.Date;
public class Snowflake implements Serializable {
private static final long serialVersionUID = 1L;
private final long twepoch;
private final long workerIdBits = 5L;
/**
* 默认的起始时间为Thu, 04 Nov 2010 01:42:54 GMT
*/
public static long DEFAULT_TWEPOCH = 1288834974657L;
/**
* 默认回拨时间2S
*/
public static long DEFAULT_TIME_OFFSET = 2000L;
private static final long WORKER_ID_BITS = 5L;
// 最大支持机器节点数0~31一共32个
@SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long dataCenterIdBits = 5L;
private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
private static final long DATA_CENTER_ID_BITS = 5L;
// 最大支持数据中心节点数0~31一共32个
@SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
// 序列号12位
private final long sequenceBits = 12L;
private static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
// 序列号12位表示只允许workId的范围为0-4095
private static final long SEQUENCE_BITS = 12L;
// 机器节点左移12位
private final long workerIdShift = sequenceBits;
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
// 数据中心节点左移17位
private final long dataCenterIdShift = sequenceBits + workerIdBits;
private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
// 时间毫秒数左移22位
private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
// 序列掩码用于限定序列最大值不能超过4095
@SuppressWarnings("FieldCanBeLocal")
private final long sequenceMask = ~(-1L << sequenceBits);// 4095
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);// 4095
private final long twepoch;
private final long workerId;
private final long dataCenterId;
private final boolean useSystemClock;
// 允许的时钟回拨数
private final long timeOffset;
private long sequence = 0L;
private long lastTimestamp = -1L;
/**
* 构造使用自动生成的工作节点ID和数据中心ID
*/
public Snowflake() {
this(IdUtil.getWorkerId(IdUtil.getDataCenterId(MAX_DATA_CENTER_ID), MAX_WORKER_ID)
, IdUtil.getDataCenterId(MAX_DATA_CENTER_ID));
}
/**
* 构造
*
* @param workerId 终端ID
*/
public Snowflake(long workerId) {
this(workerId, IdUtil.getDataCenterId(MAX_DATA_CENTER_ID));
}
/**
* 构造
*
@ -87,21 +120,34 @@ public class Snowflake implements Serializable {
* @since 5.1.3
*/
public Snowflake(Date epochDate, long workerId, long dataCenterId, boolean isUseSystemClock) {
this(epochDate, workerId, dataCenterId, isUseSystemClock, DEFAULT_TIME_OFFSET);
}
/**
* @param epochDate 初始化时间起点null表示默认起始日期,后期修改会导致id重复,如果要修改连workerId dataCenterId慎用
* @param workerId 工作机器节点id
* @param dataCenterId 数据中心id
* @param isUseSystemClock 是否使用{@link SystemClock} 获取当前时间戳
* @param timeOffset 允许时间回拨的毫秒数
* @since 5.7.3
*/
public Snowflake(Date epochDate, long workerId, long dataCenterId, boolean isUseSystemClock, long timeOffset) {
if (null != epochDate) {
this.twepoch = epochDate.getTime();
} else{
// Thu, 04 Nov 2010 01:42:54 GMT
this.twepoch = 1288834974657L;
this.twepoch = DEFAULT_TWEPOCH;
}
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(StrUtil.format("worker Id can't be greater than {} or less than 0", maxWorkerId));
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(StrUtil.format("worker Id can't be greater than {} or less than 0", MAX_WORKER_ID));
}
if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
throw new IllegalArgumentException(StrUtil.format("datacenter Id can't be greater than {} or less than 0", maxDataCenterId));
if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
throw new IllegalArgumentException(StrUtil.format("datacenter Id can't be greater than {} or less than 0", MAX_DATA_CENTER_ID));
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
this.useSystemClock = isUseSystemClock;
this.timeOffset = timeOffset;
}
/**
@ -111,7 +157,7 @@ public class Snowflake implements Serializable {
* @return 所属机器的id
*/
public long getWorkerId(long id) {
return id >> workerIdShift & ~(-1L << workerIdBits);
return id >> WORKER_ID_SHIFT & ~(-1L << WORKER_ID_BITS);
}
/**
@ -121,7 +167,7 @@ public class Snowflake implements Serializable {
* @return 所属数据中心
*/
public long getDataCenterId(long id) {
return id >> dataCenterIdShift & ~(-1L << dataCenterIdBits);
return id >> DATA_CENTER_ID_SHIFT & ~(-1L << DATA_CENTER_ID_BITS);
}
/**
@ -131,7 +177,7 @@ public class Snowflake implements Serializable {
* @return 生成的时间
*/
public long getGenerateDateTime(long id) {
return (id >> timestampLeftShift & ~(-1L << 41L)) + twepoch;
return (id >> TIMESTAMP_LEFT_SHIFT & ~(-1L << 41L)) + twepoch;
}
/**
@ -142,8 +188,8 @@ public class Snowflake implements Serializable {
public synchronized long nextId() {
long timestamp = genTime();
if (timestamp < this.lastTimestamp) {
if(this.lastTimestamp - timestamp < 2000){
// 容忍2秒内的回拨避免NTP校时造成的异常
if(this.lastTimestamp - timestamp < timeOffset){
// 容忍指定的回拨避免NTP校时造成的异常
timestamp = lastTimestamp;
} else{
// 如果服务器时间有问题(时钟后退) 报错
@ -152,7 +198,7 @@ public class Snowflake implements Serializable {
}
if (timestamp == this.lastTimestamp) {
final long sequence = (this.sequence + 1) & sequenceMask;
final long sequence = (this.sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
@ -163,7 +209,10 @@ public class Snowflake implements Serializable {
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) | (dataCenterId << dataCenterIdShift) | (workerId << workerIdShift) | sequence;
return ((timestamp - twepoch) << TIMESTAMP_LEFT_SHIFT)
| (dataCenterId << DATA_CENTER_ID_SHIFT)
| (workerId << WORKER_ID_SHIFT)
| sequence;
}
/**

View File

@ -544,15 +544,7 @@ public class NetUtil {
return null;
}
byte[] mac = null;
try {
final NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
if (null != networkInterface) {
mac = networkInterface.getHardwareAddress();
}
} catch (SocketException e) {
throw new UtilException(e);
}
final byte[] mac = getHardwareAddress(inetAddress);
if (null != mac) {
final StringBuilder sb = new StringBuilder();
String s;
@ -570,6 +562,39 @@ public class NetUtil {
return null;
}
/**
* 获得本机物理地址
*
* @return 本机物理地址
* @since 5.7.3
*/
public static byte[] getLocalHardwareAddress() {
return getHardwareAddress(getLocalhost());
}
/**
* 获得指定地址信息中的硬件地址
*
* @param inetAddress {@link InetAddress}
* @return 硬件地址
* @since 5.7.3
*/
public static byte[] getHardwareAddress(InetAddress inetAddress) {
if (null == inetAddress) {
return null;
}
try {
final NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
if (null != networkInterface) {
return networkInterface.getHardwareAddress();
}
} catch (SocketException e) {
throw new UtilException(e);
}
return null;
}
/**
* 获取主机名称一次获取会缓存名称
*

View File

@ -1,9 +1,11 @@
package cn.hutool.core.util;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.lang.Singleton;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.net.NetUtil;
/**
* ID生成器工具类此工具类中主要封装
@ -134,4 +136,53 @@ public class IdUtil {
public static Snowflake getSnowflake(long workerId, long datacenterId) {
return Singleton.get(Snowflake.class, workerId, datacenterId);
}
/**
* 获取数据中心ID<br>
* 数据中心ID依赖于本地网卡MAC地址
* <p>
* 此算法来自于mybatis-plus#Sequence
* </p>
*
* @param maxDatacenterId 最大的中心ID
* @return 数据中心ID
* @since 5.7.3
*/
public static long getDataCenterId(long maxDatacenterId) {
long id = 1L;
final byte[] mac = NetUtil.getLocalHardwareAddress();
if (null != mac) {
id = ((0x000000FF & (long) mac[mac.length - 2])
| (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
return id;
}
/**
* 获取机器ID使用进程ID配合数据中心ID生成<br>
* 机器依赖于本进程ID或进程名的Hash值
*
* <p>
* 此算法来自于mybatis-plus#Sequence
* </p>
*
* @param datacenterId 数据中心ID
* @param maxWorkerId 最大的机器节点ID
* @since 5.7.3
*/
public static long getWorkerId(long datacenterId, long maxWorkerId) {
final StringBuilder mpid = new StringBuilder();
mpid.append(datacenterId);
try {
mpid.append(RuntimeUtil.getPid());
} catch (UtilException igonre) {
//ignore
}
/*
* MAC + PID hashcode 获取16个低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
}

View File

@ -1,5 +1,6 @@
package cn.hutool.core.util;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrBuilder;
@ -7,6 +8,7 @@ import cn.hutool.core.text.StrBuilder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@ -279,12 +281,33 @@ public class RuntimeUtil {
return getMaxMemory() - getTotalMemory() + getFreeMemory();
}
/**
* 获取当前进程ID首先获取进程名称读取@前的ID值如果不存在则读取进程名的hash值
*
* @return 进程ID
* @throws UtilException 进程名称为空
* @since 5.7.3
*/
public static int getPid() throws UtilException {
final String processName = ManagementFactory.getRuntimeMXBean().getName();
if (StrUtil.isBlank(processName)) {
throw new UtilException("Process name is blank!");
}
final int atIndex = processName.indexOf('@');
if (atIndex > 0) {
return Integer.parseInt(processName.substring(0, atIndex));
} else {
return processName.hashCode();
}
}
/**
* 处理命令多行命令原样返回单行命令拆分处理
*
* @param cmds 命令
* @return 处理后的命令
*/
private static String[] handleCmds(String... cmds){
private static String[] handleCmds(String... cmds) {
if (ArrayUtil.isEmpty(cmds)) {
throw new NullPointerException("Command is empty !");
}
@ -306,7 +329,7 @@ public class RuntimeUtil {
* @param cmd 命令 git commit -m 'test commit'
* @return 分割后的命令
*/
private static String[] cmdSplit(String cmd){
private static String[] cmdSplit(String cmd) {
final List<String> cmds = new ArrayList<>();
final int length = cmd.length();
@ -317,27 +340,27 @@ public class RuntimeUtil {
char c;
for (int i = 0; i < length; i++) {
c = cmd.charAt(i);
switch (c){
switch (c) {
case CharUtil.SINGLE_QUOTE:
case CharUtil.DOUBLE_QUOTES:
if(inWrap){
if(c == stack.peek()){
if (inWrap) {
if (c == stack.peek()) {
//结束包装
stack.pop();
inWrap = false;
}
cache.append(c);
} else{
} else {
stack.push(c);
cache.append(c);
inWrap = true;
}
break;
case CharUtil.SPACE:
if(inWrap){
if (inWrap) {
// 处于包装内
cache.append(c);
} else{
} else {
cmds.add(cache.toString());
cache.reset();
}
@ -348,7 +371,7 @@ public class RuntimeUtil {
}
}
if(cache.hasContent()){
if (cache.hasContent()) {
cmds.add(cache.toString());
}

View File

@ -134,4 +134,10 @@ public class IdUtilTest {
}
Assert.assertEquals(threadCount * idCountPerThread, set.size());
}
@Test
public void getDataCenterIdTest(){
final long dataCenterId = IdUtil.getDataCenterId(Long.MAX_VALUE);
Assert.assertTrue(dataCenterId > 1);
}
}