using Pole.Core.Abstraction;
using System;
using System.Collections.Generic;
using System.Text;
namespace Pole.Core.Utils.Abstraction
{
public class SnowflakeIdGenerator : ISnowflakeIdGenerator
{
private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private int generatorIdBits;
private long twepoch;
private int maxGeneratorId;
private const int SequenceAndGeneratorIdBits = 64 - 1 - 41;
///
/// 这里的位数决定 每毫秒能生成的最大个数
///
private int sequenceBits;
private int generatorIdShift;
private const int TimestampLeftShift = SequenceAndGeneratorIdBits;
private long sequenceMask;
public long GeneratorId { get; private set; }
// 毫秒内序列(0~4095)
public long Sequence { get; private set; }
// 上次生成ID的时间截
public long LastTimestamp { get; private set; }
///
/// 时间戳为41位,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
///
///
///
public SnowflakeIdGenerator(DateTime beginTime, int generatorIdBits, long generatorId)
{
twepoch = Convert.ToInt64((beginTime.ToUniversalTime() - Jan1st1970).TotalMilliseconds);
this.generatorIdBits = generatorIdBits;
maxGeneratorId = -1 ^ (-1 << this.generatorIdBits);
sequenceBits = SequenceAndGeneratorIdBits - generatorIdBits;
generatorIdShift = sequenceBits;
sequenceMask = -1L ^ (-1L << sequenceBits);
GeneratorId = generatorId;
Sequence = 0L;
LastTimestamp = -1L;
}
public string NextId()
{
lock (this)
{
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 - twepoch) << TimestampLeftShift)
| (GeneratorId << generatorIdShift)
| Sequence;
return id.ToString();
}
}
private static long GetCurrentTimestamp()
{
return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
}
private static long GetNextTimestamp(long lastTimestamp)
{
long timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp();
}
return timestamp;
}
}
}