Created
September 30, 2018 07:00
-
-
Save liutf/4fbf081496f3fee9ccfa12f8ae7c1661 to your computer and use it in GitHub Desktop.
生成分布式ID-雪花算法
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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