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 * 【core 】 增加Convert.toSet方法issue#I3XFG2@Gitee
@ -14,6 +14,7 @@
* 【core 】 增加可自定义日期格式GlobalCustomFormat * 【core 】 增加可自定义日期格式GlobalCustomFormat
* 【jwt 】 JWT修改默认有序并规定payload日期格式为秒数 * 【jwt 】 JWT修改默认有序并规定payload日期格式为秒数
* 【json 】 增加JSONWriter * 【json 】 增加JSONWriter
* 【core 】 IdUtil增加getWorkerId和getDataCenterIdissueI3Y5NI@Gitee
### 🐞Bug修复 ### 🐞Bug修复
* 【json 】 修复XML转义字符的问题issue#I3XH09@Gitee * 【json 】 修复XML转义字符的问题issue#I3XH09@Gitee

View File

@ -1,16 +1,16 @@
package cn.hutool.core.lang; 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.date.DateUtil;
import cn.hutool.core.util.ClassLoaderUtil; import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.StrUtil; 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> * MongoDB ID生成策略实现<br>
* ObjectId由以下几部分组成 * ObjectId由以下几部分组成
@ -156,14 +156,7 @@ public class ObjectId {
// 进程ID初始化 // 进程ID初始化
int processId; int processId;
try { try {
// 获取进程ID processId = RuntimeUtil.getPid();
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();
}
} catch (Throwable t) { } catch (Throwable t) {
processId = RandomUtil.randomInt(); processId = RandomUtil.randomInt();
} }

View File

@ -1,6 +1,7 @@
package cn.hutool.core.lang; package cn.hutool.core.lang;
import cn.hutool.core.date.SystemClock; import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import java.io.Serializable; import java.io.Serializable;
@ -14,6 +15,7 @@ import java.util.Date;
* snowflake的结构如下(每部分用-分开):<br> * snowflake的结构如下(每部分用-分开):<br>
* *
* <pre> * <pre>
* 符号位1bit- 时间戳相对值41bit- 数据中心标志5bit- 机器标志5bit- 递增序号12bit
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* </pre> * </pre>
* <p> * <p>
@ -23,7 +25,8 @@ import java.util.Date;
* <p> * <p>
* 并且可以通过生成的id反推出生成时间,datacenterId和workerId * 并且可以通过生成的id反推出生成时间,datacenterId和workerId
* <p> * <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 * @author Looly
* @since 3.0.1 * @since 3.0.1
@ -31,33 +34,63 @@ import java.util.Date;
public class Snowflake implements Serializable { public class Snowflake implements Serializable {
private static final long serialVersionUID = 1L; 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个 // 最大支持机器节点数0~31一共32个
@SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"}) @SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
private final long dataCenterIdBits = 5L; private static final long DATA_CENTER_ID_BITS = 5L;
// 最大支持数据中心节点数0~31一共32个 // 最大支持数据中心节点数0~31一共32个
@SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"}) @SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits); private static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
// 序列号12位 // 序列号12位表示只允许workId的范围为0-4095
private final long sequenceBits = 12L; private static final long SEQUENCE_BITS = 12L;
// 机器节点左移12位 // 机器节点左移12位
private final long workerIdShift = sequenceBits; private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
// 数据中心节点左移17位 // 数据中心节点左移17位
private final long dataCenterIdShift = sequenceBits + workerIdBits; private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
// 时间毫秒数左移22位 // 时间毫秒数左移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 // 序列掩码用于限定序列最大值不能超过4095
@SuppressWarnings("FieldCanBeLocal") @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 workerId;
private final long dataCenterId; private final long dataCenterId;
private final boolean useSystemClock; private final boolean useSystemClock;
// 允许的时钟回拨数
private final long timeOffset;
private long sequence = 0L; private long sequence = 0L;
private long lastTimestamp = -1L; 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 * @since 5.1.3
*/ */
public Snowflake(Date epochDate, long workerId, long dataCenterId, boolean isUseSystemClock) { 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) { if (null != epochDate) {
this.twepoch = epochDate.getTime(); this.twepoch = epochDate.getTime();
} else{ } else{
// Thu, 04 Nov 2010 01:42:54 GMT // Thu, 04 Nov 2010 01:42:54 GMT
this.twepoch = 1288834974657L; this.twepoch = DEFAULT_TWEPOCH;
} }
if (workerId > maxWorkerId || workerId < 0) { if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(StrUtil.format("worker Id can't be greater than {} or less than 0", maxWorkerId)); throw new IllegalArgumentException(StrUtil.format("worker Id can't be greater than {} or less than 0", MAX_WORKER_ID));
} }
if (dataCenterId > maxDataCenterId || dataCenterId < 0) { if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
throw new IllegalArgumentException(StrUtil.format("datacenter Id can't be greater than {} or less than 0", maxDataCenterId)); throw new IllegalArgumentException(StrUtil.format("datacenter Id can't be greater than {} or less than 0", MAX_DATA_CENTER_ID));
} }
this.workerId = workerId; this.workerId = workerId;
this.dataCenterId = dataCenterId; this.dataCenterId = dataCenterId;
this.useSystemClock = isUseSystemClock; this.useSystemClock = isUseSystemClock;
this.timeOffset = timeOffset;
} }
/** /**
@ -111,7 +157,7 @@ public class Snowflake implements Serializable {
* @return 所属机器的id * @return 所属机器的id
*/ */
public long getWorkerId(long 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 所属数据中心 * @return 所属数据中心
*/ */
public long getDataCenterId(long id) { 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 生成的时间 * @return 生成的时间
*/ */
public long getGenerateDateTime(long id) { 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() { public synchronized long nextId() {
long timestamp = genTime(); long timestamp = genTime();
if (timestamp < this.lastTimestamp) { if (timestamp < this.lastTimestamp) {
if(this.lastTimestamp - timestamp < 2000){ if(this.lastTimestamp - timestamp < timeOffset){
// 容忍2秒内的回拨避免NTP校时造成的异常 // 容忍指定的回拨避免NTP校时造成的异常
timestamp = lastTimestamp; timestamp = lastTimestamp;
} else{ } else{
// 如果服务器时间有问题(时钟后退) 报错 // 如果服务器时间有问题(时钟后退) 报错
@ -152,7 +198,7 @@ public class Snowflake implements Serializable {
} }
if (timestamp == this.lastTimestamp) { if (timestamp == this.lastTimestamp) {
final long sequence = (this.sequence + 1) & sequenceMask; final long sequence = (this.sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) { if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp); timestamp = tilNextMillis(lastTimestamp);
} }
@ -163,7 +209,10 @@ public class Snowflake implements Serializable {
lastTimestamp = timestamp; 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; return null;
} }
byte[] mac = null; final byte[] mac = getHardwareAddress(inetAddress);
try {
final NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
if (null != networkInterface) {
mac = networkInterface.getHardwareAddress();
}
} catch (SocketException e) {
throw new UtilException(e);
}
if (null != mac) { if (null != mac) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
String s; String s;
@ -570,6 +562,39 @@ public class NetUtil {
return null; 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; package cn.hutool.core.util;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.ObjectId; import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.lang.Singleton; import cn.hutool.core.lang.Singleton;
import cn.hutool.core.lang.Snowflake; import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.UUID; import cn.hutool.core.lang.UUID;
import cn.hutool.core.net.NetUtil;
/** /**
* ID生成器工具类此工具类中主要封装 * ID生成器工具类此工具类中主要封装
@ -134,4 +136,53 @@ public class IdUtil {
public static Snowflake getSnowflake(long workerId, long datacenterId) { public static Snowflake getSnowflake(long workerId, long datacenterId) {
return Singleton.get(Snowflake.class, workerId, 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; package cn.hutool.core.util;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrBuilder; import cn.hutool.core.text.StrBuilder;
@ -7,6 +8,7 @@ import cn.hutool.core.text.StrBuilder;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -279,8 +281,29 @@ public class RuntimeUtil {
return getMaxMemory() - getTotalMemory() + getFreeMemory(); 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 命令 * @param cmds 命令
* @return 处理后的命令 * @return 处理后的命令
*/ */

View File

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