using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Wood.Util; namespace Wood.Util { /// /// 雪花id /// public class Snowflake { private const long TwEpoch = 1546272000000L;//2019-01-01 00:00:00 private const int WorkerIdBits = 5; 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 int WorkerIdShift = SequenceBits; private const int DatacenterIdShift = SequenceBits + WorkerIdBits; private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; private const long SequenceMask = -1L ^ (-1L << SequenceBits); private long _sequence = 0L; private long _lastTimestamp = -1L; /// ///10位的数据机器位中的高位 /// public long WorkerId { get; protected set; } /// /// 10位的数据机器位中的低位 /// public long DatacenterId { get; protected set; } private readonly object _lock = new object(); /// /// 基于Twitter的snowflake算法 /// /// 10位的数据机器位中的高位,默认不应该超过5位(5byte) /// 10位的数据机器位中的低位,默认不应该超过5位(5byte) /// 初始序列 public Snowflake(long workerId, long datacenterId, long sequence = 0L) { WorkerId = workerId; DatacenterId = datacenterId; _sequence = sequence; if (workerId > MaxWorkerId || workerId < 0) { throw new ArgumentException($"worker Id can't be greater than {MaxWorkerId} or less than 0"); } if (datacenterId > MaxDatacenterId || datacenterId < 0) { throw new ArgumentException($"datacenter Id can't be greater than {MaxDatacenterId} or less than 0"); } } public long CurrentId { get; private set; } /// /// 获取下一个Id,该方法线程安全 /// /// public long NextId() { lock (_lock) { var timestamp = DateTimeHelper.GetUnixTimeStamp(DateTime.Now); if (timestamp < _lastTimestamp) { //TODO 是否可以考虑直接等待? throw new Exception( $"Clock moved backwards or wrapped around. Refusing to generate id for {_lastTimestamp - timestamp} ticks"); } if (_lastTimestamp == timestamp) { _sequence = (_sequence + 1) & SequenceMask; if (_sequence == 0) { timestamp = TilNextMillis(_lastTimestamp); } } else { _sequence = 0; } _lastTimestamp = timestamp; CurrentId = ((timestamp - TwEpoch) << TimestampLeftShift) | (DatacenterId << DatacenterIdShift) | (WorkerId << WorkerIdShift) | _sequence; return CurrentId; } } private long TilNextMillis(long lastTimestamp) { var timestamp = DateTimeHelper.GetUnixTimeStamp(DateTime.Now); while (timestamp <= lastTimestamp) { timestamp = DateTimeHelper.GetUnixTimeStamp(DateTime.Now); } return timestamp; } } }