增加对数据库主键为numberic的支持
This commit is contained in:
yubaolee
2021-03-13 16:21:56 +08:00
parent f7088c9d29
commit 9c2d044d12
38 changed files with 911 additions and 151 deletions

View File

@@ -0,0 +1,11 @@
using System;
namespace Yitter.IdGenerator
{
public interface IIdGenerator
{
Action<OverCostActionArg> GenIdActionAsync { get; set; }
long NewLong();
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Yitter.IdGenerator
{
internal interface ISnowWorker
{
Action<OverCostActionArg> GenAction { get; set; }
long NextId();
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Yitter.IdGenerator
{
public class IdGeneratorOptions
{
/// <summary>
/// 雪花计算方法
/// 1|2
/// </summary>
public short Method { get; set; } = 1;
/// <summary>
/// 开始时间UTC格式
/// 不能超过当前系统时间
/// </summary>
public DateTime StartTime { get; set; } = DateTime.MinValue;
/// <summary>
/// 机器码
/// 与 WorkerIdBitLength 有关系
/// </summary>
public ushort WorkerId { get; set; } = 0;
/// <summary>
/// 机器码位长
/// 范围2-21要求序列数位长+机器码位长不超过22
/// 建议范围6-12。
/// </summary>
public byte WorkerIdBitLength { get; set; } = 6;//10;
/// <summary>
/// 序列数位长
/// 范围2-21要求序列数位长+机器码位长不超过22
/// 建议范围6-14。
/// </summary>
public byte SeqBitLength { get; set; } = 6;//10;
/// <summary>
/// 最大序列数(含)
/// 由SeqBitLength计算的最大值
/// </summary>
public int MaxSeqNumber { get; set; } = 0;
/// <summary>
/// 最小序列数(含)
/// 默认11不小于5不大于MaxSeqNumber-2
/// </summary>
public ushort MinSeqNumber { get; set; } = 11;
/// <summary>
/// 最大漂移次数(含),
/// 默认2000推荐范围500-10000与计算能力有关
/// </summary>
public int TopOverCostCount { get; set; } = 2000;
public IdGeneratorOptions()
{
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Yitter.IdGenerator
{
public class OverCostActionArg
{
public int ActionType { get; set; }
public long TimeTick { get; set; }
public ushort WorkerId { get; set; }
public int OverCostCountInOneTerm { get; set; }
public int GenCountInOneTerm { get; set; }
public int TermIndex { get; set; }
public OverCostActionArg(ushort workerId, long timeTick, int actionType = 0, int overCostCountInOneTerm = 0, int genCountWhenOverCost = 0,int index=0)
{
ActionType = actionType;
TimeTick = timeTick;
WorkerId = workerId;
OverCostCountInOneTerm = overCostCountInOneTerm;
GenCountInOneTerm = genCountWhenOverCost;
TermIndex = index;
}
}
}

View File

@@ -0,0 +1,296 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Yitter.IdGenerator
{
/// <summary>
/// 雪花漂移算法
/// </summary>
internal class SnowWorkerM1 : ISnowWorker
{
/// <summary>
/// 基础时间
/// </summary>
protected readonly DateTime StartTimeUtc = new DateTime(2020, 2, 20, 2, 20, 2, 20, DateTimeKind.Utc);
/// <summary>
/// 机器码
/// </summary>
protected readonly ushort WorkerId = 0;
/// <summary>
/// 机器码位长
/// (机器码+序列数<=22位
/// </summary>
protected readonly byte WorkerIdBitLength = 0;
/// <summary>
/// 自增序列数位长
/// (机器码+序列数<=22位
/// </summary>
protected readonly byte SeqBitLength = 0;
/// <summary>
/// 最大序列数(含此值)
/// 超过最大值就会从MinSeqNumber开始
/// </summary>
protected readonly int MaxSeqNumber = 0;
/// <summary>
/// 最小序列数(含此值)
/// </summary>
protected readonly ushort MinSeqNumber = 0;
/// <summary>
/// 最大漂移次数
/// </summary>
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<OverCostActionArg> 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();
}
}
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Yitter.IdGenerator
{
/// <summary>
/// 常规雪花算法
/// </summary>
internal class SnowWorkerM2 : SnowWorkerM1
{
public SnowWorkerM2(IdGeneratorOptions options) : base(options)
{
}
public override long NextId()
{
lock (_SyncLock)
{
long currentTimeTick = GetCurrentTimeTick();
if (_LastTimeTick == currentTimeTick)
{
if (_CurrentSeqNumber++ > MaxSeqNumber)
{
_CurrentSeqNumber = MinSeqNumber;
currentTimeTick = GetNextTimeTick();
}
}
else
{
_CurrentSeqNumber = MinSeqNumber;
}
if (currentTimeTick < _LastTimeTick)
{
throw new Exception(string.Format("Time error for {0} milliseconds", _LastTimeTick - currentTimeTick));
}
_LastTimeTick = currentTimeTick;
var result = ((currentTimeTick << _TimestampShift) + ((long)WorkerId << SeqBitLength) + (uint)_CurrentSeqNumber);
return result;
}
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Yitter.IdGenerator
{
public class YitIdGenerator : IIdGenerator
{
private ISnowWorker _SnowWorker { get; set; }
public Action<OverCostActionArg> GenIdActionAsync
{
get => _SnowWorker.GenAction;
set => _SnowWorker.GenAction = value;
}
public YitIdGenerator(IdGeneratorOptions options)
{
if (options == null)
{
throw new ApplicationException("options error.");
}
if (options.StartTime > DateTime.Now)
{
throw new ApplicationException("StartTime error.");
}
if (options.SeqBitLength + options.WorkerIdBitLength > 22)
{
throw new ApplicationException("errorWorkerIdBitLength + SeqBitLength <= 22");
}
var maxWorkerIdNumber = Math.Pow(2, options.WorkerIdBitLength) - 1;
if (options.WorkerId < 1 || options.WorkerId > maxWorkerIdNumber)
{
throw new ApplicationException("WorkerId is error. (range:[1, " + maxWorkerIdNumber + "]");
}
if (options.SeqBitLength < 2 || options.SeqBitLength > 21)
{
throw new ApplicationException("SeqBitLength is error. (range:[2, 21])");
}
var maxSeqNumber = Math.Pow(2, options.SeqBitLength) - 1;
if (options.MaxSeqNumber < 0 || options.MaxSeqNumber > maxSeqNumber)
{
throw new ApplicationException("MaxSeqNumber is error. (range:[1, " + maxSeqNumber + "]");
}
var maxValue = maxSeqNumber - 2;
if (options.MinSeqNumber < 5 || options.MinSeqNumber > maxValue)
{
throw new ApplicationException("MinSeqNumber is error. (range:[5, " + maxValue + "]");
}
switch (options.Method)
{
case 1:
_SnowWorker = new SnowWorkerM1(options);
break;
case 2:
_SnowWorker = new SnowWorkerM2(options);
break;
default:
_SnowWorker = new SnowWorkerM1(options);
break;
}
if (options.Method == 1)
{
Thread.Sleep(500);
}
}
public long NewLong()
{
return _SnowWorker.NextId();
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Yitter.IdGenerator;
namespace Infrastructure.Test
{
class TestSnowflake
{
[Test]
public void Generate()
{
// 全局初始化设置WorkerId默认最大2^16-1。初始化过程全局只需一次且必须最先设置
var options = new IdGeneratorOptions(){ WorkerId = 1};
IIdGenerator IdHelper = new YitIdGenerator(options);
// 初始化以后就可以在需要的地方调用方法生成ID。
long val =IdHelper.NewLong();
int val2 = (int)val;
Console.WriteLine($"val:{val}/int:{val2}");
}
}
}