Files
StandardSence/StandardScene.Core/CommonTools/SnowflakeIdGenerator.cs
T

129 lines
4.8 KiB
C#
Raw Normal View History

2026-06-14 11:19:15 +08:00
using System;
using System.Threading;
namespace StandardScene.CommonTools
{
public sealed class SnowflakeIdGenerator
{
// 默认起始时间戳:2026-01-01T00:00:00.000ZUnix 毫秒)
// 如果你希望生成的数字更短,可以在构造函数里传入更“近”的 _epochMs(建议全系统统一)。
private const long DefaultEpochMs = 1767225600000L;
private const int WorkerIdBits = 5; // 机器ID所占的位数
private const int DatacenterIdBits = 5; // 数据中心ID所占的位数
private const int MaxWorkerId = -1 ^ (-1 << WorkerIdBits); // 最大机器ID
private const int MaxDatacenterId = -1 ^ (-1 << DatacenterIdBits); // 最大数据中心ID
private const int SequenceBits = 12; // 序列号所占的位数
private const int WorkerIdShift = SequenceBits; // 机器ID左移的位数
private const int DatacenterIdShift = SequenceBits + WorkerIdBits; // 数据中心ID左移的位数
private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; // 时间戳左移的位数
private const long SequenceMask = -1L ^ (-1L << SequenceBits); // 序列号的最大值
private const string Base62Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private readonly object _syncRoot = new object();
private readonly long _epochMs;
private readonly long _workerId; // 机器ID
private readonly long _datacenterId; // 数据中心ID
private long _sequence; // 序列号
private long _lastTimestamp = -1L; // 上次生成ID的时间戳
public SnowflakeIdGenerator(long workerId, long datacenterId, long? epochMs = null)
{
this._epochMs = epochMs ?? DefaultEpochMs;
if (this._epochMs > TimeGen())
{
throw new ArgumentException("_epochMs cannot be in the future.");
}
if (workerId is > MaxWorkerId or < 0)
{
throw new ArgumentException($"worker Id can't be greater than {MaxWorkerId} or less than 0");
}
if (datacenterId is > MaxDatacenterId or < 0)
{
throw new ArgumentException($"{datacenterId} can't be greater than {MaxDatacenterId} or less than 0");
}
this._workerId = workerId;
this._datacenterId = datacenterId;
}
public long NextId()
{
lock (_syncRoot)
{
long timestamp = TimeGen();
if (timestamp < _lastTimestamp)
{
// 容忍系统时钟回拨:等待到追上 _lastTimestamp,避免直接抛异常把业务打崩。
timestamp = TilNextMillis(_lastTimestamp);
}
if (_lastTimestamp == timestamp)
{
_sequence = (_sequence + 1) & SequenceMask;
if (_sequence == 0)
{
timestamp = TilNextMillis(_lastTimestamp);
}
}
else
{
_sequence = 0;
}
_lastTimestamp = timestamp;
long id = ((timestamp - _epochMs) << TimestampLeftShift)
| (_datacenterId << DatacenterIdShift)
| (_workerId << WorkerIdShift)
| _sequence;
return id;
}
}
/// <summary>
/// 生成更短的字符串形式 ID(Base62 编码),便于显示/存储。
/// </summary>
public string NextIdBase62()
{
ulong value = unchecked((ulong)NextId());
return ToBase62(value);
}
private static long TilNextMillis(long lastTimestamp)
{
var spin = new SpinWait();
long timestamp;
do
{
spin.SpinOnce();
timestamp = TimeGen();
}
while (timestamp <= lastTimestamp);
return timestamp;
}
private static long TimeGen()
{
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
private static string ToBase62(ulong value)
{
if (value == 0)
{
return "0";
}
// 2^64-1 的 base62 最大长度为 11(因为 62^11 > 2^64)。
char[] buffer = new char[11];
int pos = buffer.Length;
while (value > 0)
{
ulong rem = value % 62;
value /= 62;
buffer[--pos] = Base62Alphabet[(int)rem];
}
return new string(buffer, pos, buffer.Length - pos);
}
}
}