This commit is contained in:
sxu 2025-01-27 12:11:19 +08:00
parent 0ff7f4be20
commit 63fe0e8824
3 changed files with 329 additions and 0 deletions

View File

@ -0,0 +1,53 @@
package net.xnzn.framework.id;
import com.google.common.base.Preconditions;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class Id {
private static final long EPOCH = LocalDateTime.of(2021, 12, 29, 9, 2).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
private static final long SEQUENCE_BITS = 12L;
private static final long WORKER_ID_BITS = 10L;
static final long WORKER_ID_MAX_VALUE = 1024L;
private static final long SEQUENCE_MASK = 4095L;
private static final long WORKER_ID_LEFT_SHIFT_BITS = 12L;
private static final long TIMESTAMP_LEFT_SHIFT_BITS = 22L;
static Long WORKER_ID;
private static long SEQUENCE;
private static long LAST_TIME;
public Id(Long workerId) {
WORKER_ID = workerId;
}
public static long next() {
return nextKey();
}
public static String nextString() {
return String.valueOf(next());
}
private static synchronized long nextKey() {
long currentMillis = System.currentTimeMillis();
Preconditions.checkState(LAST_TIME <= currentMillis, "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", LAST_TIME, currentMillis);
if (LAST_TIME == currentMillis) {
if (0L == (SEQUENCE = SEQUENCE + 1L & 4095L)) {
currentMillis = waitUntilNextTime(currentMillis);
}
} else {
SEQUENCE = 0L;
}
LAST_TIME = currentMillis;
return currentMillis - EPOCH << 22 | WORKER_ID << 12 | SEQUENCE;
}
private static long waitUntilNextTime(final long lastTime) {
long time;
for(time = System.currentTimeMillis(); time <= lastTime; time = System.currentTimeMillis()) {
}
return time;
}
}

View File

@ -0,0 +1,107 @@
package net.xnzn.framework.id;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(
prefix = "id"
)
public class IdProperties {
public static final String PREFIX = "id";
private Boolean enabled = true;
private String key = "worker_id_sequence";
private String workspace;
public Boolean getEnabled() {
return this.enabled;
}
public String getKey() {
return this.key;
}
public String getWorkspace() {
return this.workspace;
}
public void setEnabled(final Boolean enabled) {
this.enabled = enabled;
}
public void setKey(final String key) {
this.key = key;
}
public void setWorkspace(final String workspace) {
this.workspace = workspace;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof IdProperties)) {
return false;
} else {
IdProperties other = (IdProperties)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$enabled = this.getEnabled();
Object other$enabled = other.getEnabled();
if (this$enabled == null) {
if (other$enabled == null) {
break label47;
}
} else if (this$enabled.equals(other$enabled)) {
break label47;
}
return false;
}
Object this$key = this.getKey();
Object other$key = other.getKey();
if (this$key == null) {
if (other$key != null) {
return false;
}
} else if (!this$key.equals(other$key)) {
return false;
}
Object this$workspace = this.getWorkspace();
Object other$workspace = other.getWorkspace();
if (this$workspace == null) {
if (other$workspace != null) {
return false;
}
} else if (!this$workspace.equals(other$workspace)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof IdProperties;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $enabled = this.getEnabled();
result = result * 59 + ($enabled == null ? 43 : $enabled.hashCode());
Object $key = this.getKey();
result = result * 59 + ($key == null ? 43 : $key.hashCode());
Object $workspace = this.getWorkspace();
result = result * 59 + ($workspace == null ? 43 : $workspace.hashCode());
return result;
}
public String toString() {
Boolean var10000 = this.getEnabled();
return "IdProperties(enabled=" + var10000 + ", key=" + this.getKey() + ", workspace=" + this.getWorkspace() + ")";
}
}

View File

@ -0,0 +1,169 @@
package net.xnzn.framework.id;
import cn.hutool.core.text.CharSequenceUtil;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Iterator;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.CollectionUtils;
@AutoConfiguration(
after = {RedisAutoConfiguration.class}
)
@ConditionalOnClass({StringRedisTemplate.class})
@EnableConfigurationProperties({IdProperties.class})
@ConditionalOnProperty(
prefix = "id",
name = {"enabled"},
havingValue = "true",
matchIfMissing = true
)
public class IdWorkAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(IdWorkAutoConfiguration.class);
private final IdProperties idProperties;
public IdWorkAutoConfiguration(StringRedisTemplate redisTemplate, IdProperties idProperties) throws IOException {
this.idProperties = idProperties;
Long workerId = this.getLocalWorkId();
try {
while(workerId == null) {
Long newWorkId = redisTemplate.opsForValue().increment(idProperties.getKey(), 1L);
this.saveLocalWorkId(String.valueOf(newWorkId));
workerId = this.getLocalWorkId();
}
} catch (Throwable var5) {
log.error("获取workID失败", var5);
throw var5;
}
if (workerId > 1024L) {
throw new RuntimeException("超过最大启动实例");
} else {
Id.WORKER_ID = workerId;
}
}
private void saveLocalWorkId(String workId) throws IOException {
File workIdHome = this.getWorkIdHome();
String var10002 = String.valueOf(workIdHome.getAbsoluteFile());
FileUtils.writeStringToFile(new File(var10002 + "/" + String.valueOf(UUID.randomUUID()) + ".lock"), workId, StandardCharsets.UTF_8);
}
private File getWorkIdHome() {
String workHome = this.idProperties.getWorkspace();
if (CharSequenceUtil.isBlank(workHome)) {
workHome = FileUtils.getUserDirectoryPath() + "/.workId/";
}
String var10002 = CharSequenceUtil.removeSuffix(workHome, "/");
return new File(var10002 + "/" + this.idProperties.getKey());
}
private Long getLocalWorkId() throws IOException {
File workIdHome = this.getWorkIdHome();
if (!workIdHome.exists()) {
return null;
} else {
Collection<File> files = FileUtils.listFiles(workIdHome, new String[]{"lock"}, false);
if (CollectionUtils.isEmpty(files)) {
return null;
} else {
Iterator var3 = files.iterator();
while(true) {
if (var3.hasNext()) {
File file = (File)var3.next();
FileChannel channel = null;
FileLock fileLock = null;
boolean var13 = false;
Long var8;
label112: {
try {
var13 = true;
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
channel = randomAccessFile.getChannel();
fileLock = channel.tryLock();
if (fileLock != null) {
var8 = Long.valueOf(randomAccessFile.readLine());
var13 = false;
break label112;
}
var13 = false;
} finally {
if (var13) {
if (fileLock == null) {
if (channel != null) {
channel.close();
}
} else {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
channel.close();
} catch (IOException var2) {
log.error("Release WorkId file lock error", var2);
}
}));
}
}
}
if (fileLock == null) {
if (channel != null) {
channel.close();
}
} else {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
channel.close();
} catch (IOException var2) {
log.error("Release WorkId file lock error", var2);
}
}));
}
continue;
}
if (fileLock == null) {
if (channel != null) {
channel.close();
}
} else {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
channel.close();
} catch (IOException var2) {
log.error("Release WorkId file lock error", var2);
}
}));
}
return var8;
}
return null;
}
}
}
}
}