using System.Reflection.PortableExecutable;
namespace ApiDemo
{
///
/// 雪花算法生成id
///
public class SnowFlake
{
///
/// 机器id所占的位数
///
private const int workerIdBits = 5;
///
/// 数据表述id所占位数
///
private const int datacenterIdBits = 5;
///
/// 支持的最大机器id,结果是31
///
private const long maxWorkerId = -1L ^ (-1L << workerIdBits);
///
/// 支持的最大数据表示id,结果是31
///
private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
///
/// 序列在id中所占的位数
///
private const int sequenceBits = 12;
///
/// 数据标识id向左移17位
///
private const int datacenterIdShift = sequenceBits + workerIdBits;
///
/// 机器ID向左移12位
///
private const int workerIdShift = sequenceBits;
private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
///
/// 生成序列的掩码
///
private const long sequenceMask = -1L ^ (-1L << sequenceBits);
///
/// 数据中心ID(0-31)
///
public long DatacenterId { get; private set; }
///
/// 工作机器ID(0-31)
///
public long WorkerId { get; private set; }
///
/// 毫秒内序列(0-4095)
///
public long Sequence { get; private set; }
///
/// 上次生成ID的时间戳
///
public long LastTimestamp { get; private set; }
///
/// 开始时间戳。首次使用前设置,否则无效,默认2010-1-1
///
public DateTime StartTimestamp { get; set; } = new DateTime(2010,1,1);
static object syncRoot = new object();
static readonly Lazy snowflake = new(() => new SnowFlake(0L,0L));
///
/// 默认金泰实例,WorkerId = 0,DatacenterId = 0
///
public static SnowFlake Instance { get; } = snowflake.Value;
///
/// 构造函数
///
/// 工作机器ID
/// 数据中心ID
///
public SnowFlake(long workerId,long datacenterId)
{
if (workerId > maxWorkerId || workerId < 0)
{
throw new ArgumentOutOfRangeException(nameof(workerId),$"不能大于{maxWorkerId}或小于0");
}
if (datacenterId > maxDatacenterId || datacenterId < 0)
{
throw new ArgumentOutOfRangeException(nameof(datacenterId), $"不能大于{maxDatacenterId}或小于0");
}
WorkerId = workerId;
DatacenterId = datacenterId;
Sequence = 0L;
LastTimestamp = -1L;
}
///
/// 获取下一个ID
///
///
public long NextId()
{
lock (syncRoot)
{
//获取当前时间戳
long timestamp = GetCurrentTimestamp();
if (timestamp > LastTimestamp)//时间戳改变,毫秒内序列重置
{
Sequence = 0L;
}
else if (timestamp == LastTimestamp)//如果是同一时间生成的,则进行毫秒内序列
{
Sequence = (Sequence + 1) & sequenceMask;
if (Sequence == 0)//毫秒内序列溢出
{
timestamp = GetNextTimestamp(LastTimestamp);//阻塞到下一个毫秒,获取新的时间戳
}
}
else//当前时间小于上一次ID生成的时间戳,证明系统时钟被回拨,此时需要做回拨处理
{
Sequence = (Sequence + 1) & sequenceMask;
if (Sequence > 0)
{
timestamp = LastTimestamp;//停留再最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题
}
else {
timestamp = LastTimestamp + 1;//直接进位到下一个毫秒
}
}
LastTimestamp = timestamp;//上次生成ID的时间戳
//移动并通过或运算拼到一起组成64位的ID
var id = (timestamp << timestampLeftShift)
| (DatacenterId << datacenterIdShift)
| (WorkerId << workerIdShift)
| Sequence;
return id;
}
}
///
/// 阻塞到下一个毫秒,直到获得新的时间戳
///
///
///
private long GetNextTimestamp(long lastTimestamp)
{
//获取当前时间戳
long timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp();
}
return timestamp;
}
///
/// 获取当前时间戳
///
///
private long GetCurrentTimestamp()
{
return (long)(DateTime.Now - StartTimestamp).TotalMilliseconds;
}
}
}