一款高性能的类雪花算法的全局ID生成器,并发环境下QPS可达820万+。
实际生成规则可根据具体需求更改
package com.kosmian.fastid;
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;
/**
* 流水号生成器
* <p>
* 总长度(22位):G + 17位时间戳 + 4位自增因子
* <p>
* 类雪花算法,适用于高并发环境。QPS实测值可达820万
*/
public final class GlobalNumberGenerator {
private final static Generator GENERATOR = new Generator();
public static String nextId() {
return GENERATOR.nextId();
}
private final static class Generator {
// ID前缀
private static final String PREFIX = "G";
// 序列号配置(13位=8192个/ms)
private static final int SEQUENCE_BITS = 13;
// 自增序列号最大值(8191)
private static final long SEQUENCE_MASK = (1L << SEQUENCE_BITS) - 1;
// 时间戳左移位
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS;
// 时间戳格式工具
private static final char[] DIGITS = "0123456789".toCharArray();
private static final long[] DIVISORS = {1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000};
// ID生成缓存器
private static final int ID_LENGTH = PREFIX.length() + 17 + 4;
private static final ThreadLocal<StringBuilder> ID_COMBINE = ThreadLocal.withInitial(
() -> new StringBuilder(ID_LENGTH).append(PREFIX));
// 时间戳缓存器
private static final ThreadLocal<Calendar> CALENDAR = ThreadLocal.withInitial(
Calendar::getInstance);
// 合并状态:高51位=时间戳,低13位=序列号
private final AtomicLong state = new AtomicLong(0L);
public String nextId() {
for (; ; ) {
long oldState = state.get();
long lastMs = oldState >>> TIMESTAMP_SHIFT;
long currentMs = System.currentTimeMillis();
// 时钟回拨处理
if (currentMs < lastMs) {
handleClockBackward(lastMs, currentMs);
continue;
}
// 时间戳相同序列号自增
long newMs, newSeq;
if (currentMs == lastMs) {
newSeq = (oldState & SEQUENCE_MASK) + 1;
if (newSeq > SEQUENCE_MASK) {
newMs = waitNextMillis(lastMs);
newSeq = 1;
} else {
newMs = lastMs;
}
} else {
newMs = currentMs;
newSeq = 1;
}
// 返回ID
if (state.compareAndSet(oldState, (newMs << TIMESTAMP_SHIFT) | newSeq)) {
return formatId(newMs, newSeq);
}
// CAS失败时
LockSupport.parkNanos(1_000);
}
}
private String formatId(long currentTimestamp, long sequence) {
StringBuilder sb = ID_COMBINE.get();
sb.setLength(PREFIX.length());// 保留前缀
// 时间戳部分(共19位)
Calendar cal = CALENDAR.get();
cal.setTimeInMillis(currentTimestamp);
// 年(4位)
sb.append(cal.get(Calendar.YEAR));
// 月日时分秒(各2位)
formatNumber(sb, cal.get(Calendar.MONTH) + 1, 2);
formatNumber(sb, cal.get(Calendar.DAY_OF_MONTH), 2);
formatNumber(sb, cal.get(Calendar.HOUR_OF_DAY), 2);
formatNumber(sb, cal.get(Calendar.MINUTE), 2);
formatNumber(sb, cal.get(Calendar.SECOND), 2);
// 毫秒(3位)
formatNumber(sb, cal.get(Calendar.MILLISECOND), 3);
// 自增序列号(4位)
formatNumber(sb, sequence, 4);
return sb.toString();
}
private void formatNumber(StringBuilder sb, long number, int numberDigits) {
for (int i = numberDigits - 1; i >= 0; i--) {
sb.append(DIGITS[(int) ((number / DIVISORS[i]) % 10)]);
}
}
private void handleClockBackward(long lastMs, long currentMs) {
final long maxBackwardMs = 1_000;
final long backwardMs = lastMs - currentMs;
if (backwardMs > maxBackwardMs) {
throw new IllegalStateException(
String.format("Clock moved backwards. Refusing to generate ID for %d ms", backwardMs));
}
long startWait = System.currentTimeMillis();
while (true) {
long nowSys = System.currentTimeMillis();
if (nowSys >= lastMs) {
return;
}
long remaining = lastMs - nowSys;
if (remaining > 0) {
LockSupport.parkNanos(Math.min(remaining, 1) * 1_000_000);
}
if (System.currentTimeMillis() - startWait > maxBackwardMs * 2) {
throw new IllegalStateException("Clock backward recovery timeout");
}
}
}
private long waitNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
int spins = 0;
while (timestamp <= lastTimestamp) {
if (++spins % 1_000 == 0) {
Thread.yield();
}
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
}
QPS压测
package com.kosmian.fastid;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Supplier;
public class QPSCounter {
private final AtomicLong counter = new AtomicLong(0);
private volatile long lastQPS = 0;
Runtime runtime = Runtime.getRuntime();
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final boolean isCountFreeInfo;
public QPSCounter() {
this(false);
}
public QPSCounter(boolean isCountFreeInfo) {
this.isCountFreeInfo = isCountFreeInfo;
// 每秒执行一次清零和记录QPS
scheduler.scheduleAtFixedRate(() -> {
lastQPS = counter.getAndSet(0); // 获取当前计数并清零
if (isCountFreeInfo) {
System.err.printf("内存: used=%,dMB, free=%,dMB, total=%,dMB%n",
(runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024,
runtime.freeMemory() / 1024 / 1024,
runtime.totalMemory() / 1024 / 1024);
}
}, 1, 1, TimeUnit.SECONDS);
}
// 记录一次请求
public void increment(Supplier<?> supplier) {
if (supplier != null) {
supplier.get();
}
counter.incrementAndGet();
}
// 获取上一秒的QPS
public long getQPS() {
return lastQPS;
}
// 关闭计数器
public void shutdown() {
scheduler.shutdown();
}
// 手动重置计数器(通常不需要,因为会自动每秒重置)
public void reset() {
counter.set(0);
lastQPS = 0;
}
public static void main(String[] args) {
QPSCounter counter = new QPSCounter(true);
AtomicLong secCounter = new AtomicLong(0);
int threadCount = 8;
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (;;) {
counter.increment(GlobalNumberGenerator::nextId);
}
}).start();
}
while (true) {
// 每秒打印QPS
LockSupport.parkNanos(1_000_000_000);
System.out.println("执行第 " + secCounter.incrementAndGet() + "s, threads: " + threadCount + ", QPS: " + counter.getQPS());
}
}
}

4万+

被折叠的 条评论
为什么被折叠?



