181 lines
5.6 KiB
C#
181 lines
5.6 KiB
C#
|
|
using System;
|
||
|
|
using System.Linq;
|
||
|
|
|
||
|
|
namespace QYZH.InteractiveMagazine.Common.Helpers
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 雪花ID生成器
|
||
|
|
/// 基于Twitter Snowflake算法实现
|
||
|
|
/// </summary>
|
||
|
|
public static class SnowflakeIdHelper
|
||
|
|
{
|
||
|
|
private static long _sequence = 0L;
|
||
|
|
private static long _lastTimestamp = -1L;
|
||
|
|
private static readonly object _lock = new object();
|
||
|
|
|
||
|
|
// 基础时间戳 (2020-01-01 00:00:00 UTC)
|
||
|
|
private const long TwEpoch = 1577836800000L;
|
||
|
|
|
||
|
|
// 机器ID位数
|
||
|
|
private const int WorkerIdBits = 5;
|
||
|
|
|
||
|
|
// 数据中心ID位数
|
||
|
|
private const int DataCenterIdBits = 5;
|
||
|
|
|
||
|
|
// 序列号位数
|
||
|
|
private const int SequenceBits = 12;
|
||
|
|
|
||
|
|
// 最大值计算
|
||
|
|
private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
|
||
|
|
private const long MaxDataCenterId = -1L ^ (-1L << DataCenterIdBits);
|
||
|
|
private const long MaxSequence = -1L ^ (-1L << SequenceBits);
|
||
|
|
|
||
|
|
// 位移偏移量
|
||
|
|
private const int WorkerIdShift = SequenceBits;
|
||
|
|
private const int DataCenterIdShift = SequenceBits + WorkerIdBits;
|
||
|
|
private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DataCenterIdBits;
|
||
|
|
|
||
|
|
private static long _workerId;
|
||
|
|
private static long _dataCenterId;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 静态构造函数,初始化机器ID和数据中心ID
|
||
|
|
/// </summary>
|
||
|
|
static SnowflakeIdHelper()
|
||
|
|
{
|
||
|
|
_workerId = GetWorkerId();
|
||
|
|
_dataCenterId = GetDataCenterId();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 初始化雪花ID生成器
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="workerId">机器ID (0-31)</param>
|
||
|
|
/// <param name="dataCenterId">数据中心ID (0-31)</param>
|
||
|
|
public static void Initialize(long workerId, long dataCenterId)
|
||
|
|
{
|
||
|
|
if (workerId < 0 || workerId > MaxWorkerId)
|
||
|
|
{
|
||
|
|
throw new ArgumentException($"机器ID必须在0-{MaxWorkerId}范围内", nameof(workerId));
|
||
|
|
}
|
||
|
|
|
||
|
|
if (dataCenterId < 0 || dataCenterId > MaxDataCenterId)
|
||
|
|
{
|
||
|
|
throw new ArgumentException($"数据中心ID必须在0-{MaxDataCenterId}范围内", nameof(dataCenterId));
|
||
|
|
}
|
||
|
|
|
||
|
|
_workerId = workerId;
|
||
|
|
_dataCenterId = dataCenterId;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 生成雪花ID
|
||
|
|
/// </summary>
|
||
|
|
/// <returns>唯一的雪花ID</returns>
|
||
|
|
public static long GenerateId()
|
||
|
|
{
|
||
|
|
lock (_lock)
|
||
|
|
{
|
||
|
|
long timestamp = GetCurrentMilliseconds();
|
||
|
|
|
||
|
|
// 时钟回拨检测
|
||
|
|
if (timestamp < _lastTimestamp)
|
||
|
|
{
|
||
|
|
throw new InvalidOperationException("时钟回拨异常,拒绝生成ID");
|
||
|
|
}
|
||
|
|
|
||
|
|
// 同一毫秒内,序列号递增
|
||
|
|
if (timestamp == _lastTimestamp)
|
||
|
|
{
|
||
|
|
_sequence = (_sequence + 1) & MaxSequence;
|
||
|
|
|
||
|
|
// 序列号溢出,等待下一毫秒
|
||
|
|
if (_sequence == 0)
|
||
|
|
{
|
||
|
|
timestamp = WaitNextMillis(_lastTimestamp);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_sequence = 0L;
|
||
|
|
}
|
||
|
|
|
||
|
|
_lastTimestamp = timestamp;
|
||
|
|
|
||
|
|
// 组装ID: 时间戳 + 数据中心ID + 机器ID + 序列号
|
||
|
|
return ((timestamp - TwEpoch) << TimestampLeftShift) |
|
||
|
|
(_dataCenterId << DataCenterIdShift) |
|
||
|
|
(_workerId << WorkerIdShift) |
|
||
|
|
_sequence;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 获取当前毫秒数
|
||
|
|
/// </summary>
|
||
|
|
private static long GetCurrentMilliseconds()
|
||
|
|
{
|
||
|
|
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 等待下一毫秒
|
||
|
|
/// </summary>
|
||
|
|
private static long WaitNextMillis(long lastTimestamp)
|
||
|
|
{
|
||
|
|
long timestamp = GetCurrentMilliseconds();
|
||
|
|
while (timestamp <= lastTimestamp)
|
||
|
|
{
|
||
|
|
timestamp = GetCurrentMilliseconds();
|
||
|
|
}
|
||
|
|
return timestamp;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 获取机器ID(基于MAC地址简单计算)
|
||
|
|
/// </summary>
|
||
|
|
private static long GetWorkerId()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string macAddress = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()
|
||
|
|
.FirstOrDefault(n => n.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up &&
|
||
|
|
n.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback)?
|
||
|
|
.GetPhysicalAddress().ToString() ?? "0";
|
||
|
|
|
||
|
|
long hash = 0;
|
||
|
|
foreach (char c in macAddress)
|
||
|
|
{
|
||
|
|
hash = (hash * 31 + c) & MaxWorkerId;
|
||
|
|
}
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 获取数据中心ID(基于机器名简单计算)
|
||
|
|
/// </summary>
|
||
|
|
private static long GetDataCenterId()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
string machineName = Environment.MachineName;
|
||
|
|
long hash = 0;
|
||
|
|
foreach (char c in machineName)
|
||
|
|
{
|
||
|
|
hash = (hash * 31 + c) & MaxDataCenterId;
|
||
|
|
}
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|