【发布时间】:2020-04-22 14:22:25
【问题描述】:
我将 sonyflake 移植到 Java,它运行良好。但是,我希望生成 12 位唯一数字,而不是生成 8 位数字。原始端口使用 16 位 machineId。因为我们至少有 2 个数据中心,但不限于,我为数据中心添加了 8 位 - 使用 IP 地址的第二个八位字节。我调整了位长的所有设置,无法生成 12 位数字。是否有受 sonyflake 或 Twitters Snowflake 启发的算法来生成使用 16 位 machineId 和 8 位 dataCenterId 的唯一 12 位数字?
注意:由于公司政策,我不能在此处发布我的原始 Java 端口。
编辑:这是我想出的。但是,它不是生成 12 位十进制数字,而是生成 10 位或 11 位数字。我可以对其进行哪些更改以使其始终返回 12 位十进制数?我知道我需要更改sequence 并重新计算时间。但是,我目前想专注于生成 12 位十进制数。
public class Demo {
/// 17 time + 4 dc + 10 machine + 8 sequence
private static final int BIT_LEN_TIME = 17;
private static final long BIT_WORKER_ID = 10L;
private static final long BIT_LEN_DC = 4L;
private static final long BIT_LEN_SEQUENCE = 8L;
private static final int MAX_WORKER_ID = (int) (Math.pow(2, BIT_WORKER_ID) - 1);
private static final long MAX_SEQUENCE = (int) (Math.pow(2, BIT_LEN_SEQUENCE) - 1);
private static final double FLAKE_TIME_UNIT = 1e7; // nsec, i.e. 10 msec
private static final double LEN_LIMIT = 1e11;
private static final int START_SEQ = 0;
private final ReentrantLock mutex = new ReentrantLock();
private final Instant startInstant;
private final long startTime;
private final long dc;
private long sequence;
private long lastElapsedTime;
private long worker;
public Demo(Instant startInstant) {
Objects.requireNonNull(startInstant, "startInstant cannot be null");
if (startInstant.isBefore(Instant.EPOCH) || startInstant.isAfter(Instant.now())) {
throw new Exception("Base time should be after UNIX EPOCH, or before current time.");
}
this.startInstant = startInstant;
this.startTime = this.toEverestFlakeTime(startInstant);
this.sequence = START_SEQ;
this.dc = this.msb(this.getDcId()); // 4 bits at most
this.worker = this.workerId() & ((1 << BIT_WORKER_ID) - 1); // 10 bits at most
}
public long next() {
long currentElapsedTime = this.currentElapsedTime(this.startTime);
mutex.lock();
long time = currentElapsedTime & ((1 << BIT_LEN_TIME) - 1); // 17 bits at most
if (this.sequence == MAX_SEQUENCE) {
this.sequence = START_SEQ;
System.out.println("time = " + time);
sleepMicro(currentElapsedTime - this.lastElapsedTime);
time = this.currentElapsedTime(this.startTime) & ((1 << BIT_LEN_TIME) - 1);
System.out.println("time = " + time);
} else {
// less than 15000
if((currentElapsedTime - this.lastElapsedTime) < 0x3a98) {
sleepMicro(currentElapsedTime - this.lastElapsedTime);
time = this.currentElapsedTime(this.startTime) & ((1 << BIT_LEN_TIME) - 1);
}
this.sequence += (START_SEQ + 1) & MAX_SEQUENCE;
}
long id = (time << BIT_LEN_TIME) |
(worker << BIT_WORKER_ID) |
(dc << BIT_LEN_DC) |
(sequence << BIT_LEN_SEQUENCE);
id += LEN_LIMIT;
this.lastElapsedTime = currentElapsedTime;
mutex.unlock();
return id;
}
private void sleepNano(long sleepTime) {
try {
System.out.println("nano sleeping for: " + sleepTime);
TimeUnit.NANOSECONDS.sleep(sleepTime);
} catch (Exception e) {
//
}
}
private void sleepMicro(long sleepTime) {
try {
System.out.println("micro sleeping for: " + sleepTime);
TimeUnit.MICROSECONDS.sleep(sleepTime/100);
} catch (Exception e) {
//
}
}
private long toEverestFlakeTime(Instant startInstant) {
return unixNano(startInstant);
}
private long unixNano(Instant startInstant) {
return NanoClock.systemUTC().nanos(startInstant);
}
private long currentElapsedTime(long startTime) {
return this.toEverestFlakeTime(NanoClock.systemUTC().instant()) - startTime;
}
private long msb(long n) {
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
n >>>= 1;
n += 1;
return n;
}
private int workerId() {
return new SecureRandom().nextInt(MAX_WORKER_ID);
}
private int getDcId() {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("google.com", 80));
byte[] a = socket.getLocalAddress().getAddress();
socket.close();
return Byte.toUnsignedInt(a[1]);
} catch (Exception e) {
String message = "Failed to process machine id.";
throw new EverestFlakeException(message, e);
}
}
}
【问题讨论】:
-
您介意分享您的解决方案的伪代码吗?