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; } } }