mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add getWorkerId stc
This commit is contained in:
parent
8e82919464
commit
16910ed709
@ -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和getDataCenterId(issueI3Y5NI@Gitee)
|
||||
|
||||
### 🐞Bug修复
|
||||
* 【json 】 修复XML转义字符的问题(issue#I3XH09@Gitee)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机名称,一次获取会缓存名称
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,8 +281,29 @@ 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 处理后的命令
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user