Skip to content

Instantly share code, notes, and snippets.

@liutf
Created September 30, 2018 07:00
Show Gist options
  • Save liutf/4fbf081496f3fee9ccfa12f8ae7c1661 to your computer and use it in GitHub Desktop.
Save liutf/4fbf081496f3fee9ccfa12f8ae7c1661 to your computer and use it in GitHub Desktop.
生成分布式ID-雪花算法
package com.net263.server.service.impl;
import com.ctrip.framework.foundation.internals.Utils;
import com.ctrip.framework.foundation.internals.io.BOMInputStream;
import com.net263.server.service.interfaces.IdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
/**
* FileName: SnowflakeIdGenerator
* Description:
* Version: v1.0.0
* Author: liutf
* Date: 2017/8/15
*/
@Service
@Slf4j
public class SnowflakeIdGenerator implements IdGenerator {
private class Sequence {
private final int value;
private final long timestamp;
Sequence(int value, long timestamp) {
this.value = value;
this.timestamp = timestamp;
}
int getValue() {
return value;
}
long getTimestamp() {
return timestamp;
}
long getId() {
return ((timestamp - TWEPOCH) << TIMESTAMP_SHIFT) | (instanceId << INSTANCE_ID_SHIFT) | value;
}
}
private final long TWEPOCH = 1483200000000L;
private final int INSTANCE_ID_BITS = 6;
private final int SEQUENCE_BITS = 10;
private final int INSTANCE_ID_SHIFT = SEQUENCE_BITS;
private final int TIMESTAMP_SHIFT = SEQUENCE_BITS + INSTANCE_ID_BITS;
private final int SEQUENCE_MASK = ~(-1 << SEQUENCE_BITS);
private final AtomicReference<Sequence> sequence = new AtomicReference<>();
private Integer instanceId = 0;
private static final String SERVER_PROPERTIES_LINUX = "/opt/settings/server.properties";
private static final String SERVER_PROPERTIES_WINDOWS = "C:/opt/settings/server.properties";
private Properties serverProperties = new Properties();
// @PostConstruct
// public void init() {
//// 默认写死了实例ID为0,可通过Consul来自动分配实例号
// initialize();
//// instanceId = Integer.valueOf(System.getProperty("inst_id"));
// System.out.println(instanceId);
// }
@PostConstruct
public void initialize() {
//默认写死了实例ID为0,可通过Consul来自动分配实例号
try {
String path = Utils.isOSWindows() ? SERVER_PROPERTIES_WINDOWS : SERVER_PROPERTIES_LINUX;
File file = new File(path);
if (file.exists() && file.canRead()) {
log.info("Loading {}", file.getAbsolutePath());
FileInputStream fis = new FileInputStream(file);
initialize(fis);
return;
}
log.warn("{} does not exist or is not readable.", path);
initialize(null);
} catch (Throwable ex) {
log.error("Initialize instanceId failed.", ex);
}
}
public void initialize(InputStream in) {
try {
if (in != null) {
try {
serverProperties.load(new InputStreamReader(new BOMInputStream(in), StandardCharsets.UTF_8));
} finally {
in.close();
}
}
initInstId();
} catch (Throwable ex) {
log.error("Initialize DefaultServerProvider failed.", ex);
}
}
private void initInstId() {
// 1. Try to get environment from JVM system property
String mInstId = System.getProperty("inst_id");
if (!Utils.isBlank(mInstId)) {
mInstId = mInstId.trim();
log.info("Environment is set to [{}] by JVM system property 'inst_id'.", mInstId);
return;
}
// 2. Try to get environment from OS environment variable
mInstId = System.getenv("INST_ID");
if (!Utils.isBlank(mInstId)) {
mInstId = mInstId.trim();
log.info("Environment is set to [{}] by OS env variable 'INST_ID'.", mInstId);
return;
}
// 3. Try to get environment from file "server.properties"
mInstId = serverProperties.getProperty("inst_id");
if (!Utils.isBlank(mInstId)) {
mInstId = mInstId.trim();
log.info("Environment is set to [{}] by property 'inst_id' in server.properties.", mInstId);
return;
}
// 4. Set environment to null.
if(!Utils.isBlank(mInstId)){
instanceId = Integer.parseInt(mInstId);
log.warn("instanceId is set to [{}]",instanceId);
}else{
log.warn("instanceId is set to null. Because it is not available in either (1) JVM system property 'inst_id', (2) OS env variable 'INST_ID' nor (3) property 'inst_id' from the properties InputStream.");
}
}
@Override
public Long next() {
Sequence currentSequence, nextSequence;
do {
currentSequence = sequence.get();
long currentTimestamp = currentTimestamp();
if (currentSequence == null || currentSequence.getTimestamp() < currentTimestamp) {
nextSequence = new Sequence(0, currentTimestamp);
} else if (currentSequence.getTimestamp() == currentTimestamp) {
int nextValue = (currentSequence.getValue()) + 1 & SEQUENCE_MASK;
if (nextValue == 0) {
currentTimestamp = waitForNextTimestamp();
}
nextSequence = new Sequence(nextValue, currentTimestamp);
} else {
String format = String.format("Clock is moving backwards. Rejecting requests for %d milliseconds.",
currentSequence.getTimestamp() - currentTimestamp);
throw new RuntimeException(format);
}
} while(!sequence.compareAndSet(currentSequence, nextSequence));
return nextSequence.getId();
}
private long currentTimestamp() {
return System.currentTimeMillis();
}
private long waitForNextTimestamp() {
while (currentTimestamp() <= sequence.get().getTimestamp()) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
return currentTimestamp();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment