using System; using System.Threading; using System.Threading.Tasks; namespace Yitter.IdGenerator { /// /// 雪花漂移算法 /// internal class SnowWorkerM1 : ISnowWorker { /// /// 基础时间 /// protected readonly DateTime StartTimeUtc = new DateTime(2020, 2, 20, 2, 20, 2, 20, DateTimeKind.Utc); /// /// 机器码 /// protected readonly ushort WorkerId = 0; /// /// 机器码位长 /// (机器码+序列数<=22位) /// protected readonly byte WorkerIdBitLength = 0; /// /// 自增序列数位长 /// (机器码+序列数<=22位) /// protected readonly byte SeqBitLength = 0; /// /// 最大序列数(含此值) /// 超过最大值,就会从MinSeqNumber开始 /// protected readonly int MaxSeqNumber = 0; /// /// 最小序列数(含此值) /// protected readonly ushort MinSeqNumber = 0; /// /// 最大漂移次数 /// protected readonly int TopOverCostCount = 0; protected readonly byte _TimestampShift = 0; protected static object _SyncLock = new object(); protected ushort _CurrentSeqNumber; protected long _LastTimeTick = -1L; protected long _TurnBackTimeTick = -1L; protected bool _IsOverCost = false; protected int _OverCostCountInOneTerm = 0; protected int _GenCountInOneTerm = 0; protected int _TermIndex = 0; public Action GenAction { get; set; } public SnowWorkerM1(IdGeneratorOptions options) { WorkerId = options.WorkerId; WorkerIdBitLength = options.WorkerIdBitLength; SeqBitLength = options.SeqBitLength; MaxSeqNumber = options.MaxSeqNumber; MinSeqNumber = options.MinSeqNumber; _CurrentSeqNumber = options.MinSeqNumber; TopOverCostCount = options.TopOverCostCount; if (options.StartTime != DateTime.MinValue) { StartTimeUtc = options.StartTime; } if (WorkerId < 1) { WorkerId = (ushort)DateTime.Now.Millisecond; } if (SeqBitLength == 0) { SeqBitLength = 10; } if (WorkerIdBitLength == 0) { WorkerIdBitLength = 10; } if (MaxSeqNumber == 0) { MaxSeqNumber = (int)Math.Pow(2, SeqBitLength); } _TimestampShift = (byte)(WorkerIdBitLength + SeqBitLength); } private void DoGenIdAction(OverCostActionArg arg) { Task.Run(() => { GenAction(arg); }); } private void BeginOverCostCallBack(in long useTimeTick) { if (GenAction == null) { return; } DoGenIdAction(new OverCostActionArg( WorkerId, useTimeTick, 1, _OverCostCountInOneTerm, _GenCountInOneTerm, _TermIndex)); } private void EndOverCostCallBack(in long useTimeTick) { if (_TermIndex > 10000) { _TermIndex = 0; } if (GenAction == null) { return; } DoGenIdAction(new OverCostActionArg( WorkerId, useTimeTick, 2, _OverCostCountInOneTerm, _GenCountInOneTerm, _TermIndex)); } private void TurnBackCallBack(in long useTimeTick) { if (GenAction == null) { return; } DoGenIdAction(new OverCostActionArg( WorkerId, useTimeTick, 8, _OverCostCountInOneTerm, _GenCountInOneTerm, _TermIndex)); } private long NextOverCostId() { long currentTimeTick = GetCurrentTimeTick(); if (currentTimeTick > _LastTimeTick) { EndOverCostCallBack(currentTimeTick); _LastTimeTick = currentTimeTick; _CurrentSeqNumber = MinSeqNumber; _IsOverCost = false; _OverCostCountInOneTerm = 0; _GenCountInOneTerm = 0; return CalcId(_LastTimeTick); } if (_OverCostCountInOneTerm >= TopOverCostCount) { EndOverCostCallBack(currentTimeTick); _LastTimeTick = GetNextTimeTick(); _CurrentSeqNumber = MinSeqNumber; _IsOverCost = false; _OverCostCountInOneTerm = 0; _GenCountInOneTerm = 0; return CalcId(_LastTimeTick); } if (_CurrentSeqNumber > MaxSeqNumber) { _LastTimeTick++; _CurrentSeqNumber = MinSeqNumber; _IsOverCost = true; _OverCostCountInOneTerm++; _GenCountInOneTerm++; return CalcId(_LastTimeTick); } _GenCountInOneTerm++; return CalcId(_LastTimeTick); } private long NextNormalId() { long currentTimeTick = GetCurrentTimeTick(); if (currentTimeTick > _LastTimeTick) { _LastTimeTick = currentTimeTick; _CurrentSeqNumber = MinSeqNumber; return CalcId(_LastTimeTick); } if (_CurrentSeqNumber > MaxSeqNumber) { BeginOverCostCallBack(currentTimeTick); _TermIndex++; _LastTimeTick++; _CurrentSeqNumber = MinSeqNumber; _IsOverCost = true; _OverCostCountInOneTerm++; _GenCountInOneTerm = 1; return CalcId(_LastTimeTick); } if (currentTimeTick < _LastTimeTick) { if (_TurnBackTimeTick < 1) { _TurnBackTimeTick = _LastTimeTick - 1; } Thread.Sleep(10); TurnBackCallBack(_TurnBackTimeTick); return CalcTurnBackId(_TurnBackTimeTick); } return CalcId(_LastTimeTick); } private long CalcId(in long useTimeTick) { var result = ((useTimeTick << _TimestampShift) + ((long)WorkerId << SeqBitLength) + (uint)_CurrentSeqNumber); _CurrentSeqNumber++; return result; } private long CalcTurnBackId(in long useTimeTick) { var result = ((useTimeTick << _TimestampShift) + ((long)WorkerId << SeqBitLength) + 0); _TurnBackTimeTick--; return result; } protected virtual long GetCurrentTimeTick() { return (long)(DateTime.UtcNow - StartTimeUtc).TotalMilliseconds; } protected virtual long GetNextTimeTick() { long tempTimeTicker = GetCurrentTimeTick(); while (tempTimeTicker <= _LastTimeTick) { tempTimeTicker = GetCurrentTimeTick(); } return tempTimeTicker; } public virtual long NextId() { lock (_SyncLock) { return _IsOverCost ? NextOverCostId() : NextNormalId(); } } } }