diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1e8572cdec31bcda7ea43e9291f706b3871e56d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,258 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates +*.editorconfig + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +**/.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +*.snupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + + +# macOS +.DS_Store diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000000000000000000000000000000000000..e1e5bec6d5c084221e8702843d5bad90b05af166 --- /dev/null +++ b/README.en.md @@ -0,0 +1,37 @@ +# idgenerator + +#### Description +用一种全新的雪花漂移算法,让ID更短、生成速度更快,0.1秒可生成50万个ID。 + + +#### Software Architecture +Software architecture description + +#### Installation + +1. xxxx +2. xxxx +3. xxxx + +#### Instructions + +1. xxxx +2. xxxx +3. xxxx + +#### Contribution + +1. Fork the repository +2. Create Feat_xxx branch +3. Commit your code +4. Create Pull Request + + +#### Gitee Feature + +1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md +2. Gitee blog [blog.gitee.com](https://blog.gitee.com) +3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) +4. The most valuable open source project [GVP](https://gitee.com/gvp) +5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) +6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..61d4db34c3a4908b34c04af92d505f699fffe37e --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# idgenerator + +#### 介绍 +用一种全新的雪花漂移算法,让ID更短、生成速度更快。特点: +1.ID更短,是传统算法的几倍,用50年都不会超过js(Number)的最大值。 +2.生成速度更快,0.1秒可生成50万个。(i7笔记本) +3.支持时间回拨处理。 +4.支持手工插入新ID。 +5.在算法漂移时,可抛出事件。 +6.目前是C#版,很快会出java、php等版本。 + +支持QQ群:646049993 + + +#### 软件架构 +软件架构说明 + diff --git a/auto_commit.bat b/auto_commit.bat new file mode 100644 index 0000000000000000000000000000000000000000..33727df64786cbe471fef73583ba6fd73fcf9396 --- /dev/null +++ b/auto_commit.bat @@ -0,0 +1,37 @@ +@echo off + +set result=[OK] +set tag=[OK] +set msg="auto commit" + +echo -------------------------------------------------------- + +if not "%1" == "" ( + SET msg=%1 +) + +git add -A +git commit -am %msg% + + +git push +if "%errorlevel%"=="1" goto ERR + +goto END + +:ERR + set result=[error] + set tag=[error] + +:END + + +echo %tag% result: %result% +echo ======================================================== + +if "%tag%"=="×" ( + SET __ERROR__=true + @pause +) + +:QUIT diff --git a/src/Yitter.IdGenTest/GenTest.cs b/src/Yitter.IdGenTest/GenTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..7ad84ed9d890470e4f7832f62fd3fdc35283df47 --- /dev/null +++ b/src/Yitter.IdGenTest/GenTest.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Yitter.IdGenerator; + +namespace Yitter.OrgSystem.TestA +{ + public class GenTest + { + private IIdGenerator IdGen; + private Hashtable ids = new Hashtable(); + public IList idList = new List(); + private int GenNumber; + private int WorkerId; + + + public GenTest(IIdGenerator idGen, int genNumber, int workerId) + { + GenNumber = genNumber; + IdGen = idGen; + WorkerId = workerId; + } + + public void GenId() + { + Thread t = new Thread(new ThreadStart(Gen1Start)); + t.Start(); + } + + private void Gen1Start() + { + DateTime start = DateTime.Now; + + for (int i = 0; i < GenNumber; i++) + { + var id = IdGen.NewLong(); + //ids.Add(id, i); + idList.Add(id); + } + + DateTime end = DateTime.Now; + Console.WriteLine($"++++++++++++++++++++++++++++++++++++++++WorkerId: {WorkerId}, total: {(end - start).TotalSeconds} s"); + Interlocked.Increment(ref Program.Count); + } + } +} diff --git a/src/Yitter.IdGenTest/Program.cs b/src/Yitter.IdGenTest/Program.cs new file mode 100644 index 0000000000000000000000000000000000000000..bb19b9c09617ac88faaf0e8757d4bce4e4c991c4 --- /dev/null +++ b/src/Yitter.IdGenTest/Program.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using Yitter.IdGenerator; + +namespace Yitter.OrgSystem.TestA +{ + class Program + { + static int workerCount = 1; + static int genIdCount = 50000; // 计算ID数量 + static bool single = true; + static bool outputLog = true; + static IIdGenerator IdGen = null; + static IList testList = new List(); + static bool checkResult = false; + public static int Count = 0; + + + static void Main(string[] args) + { + while (true) + { + Go(); + Thread.Sleep(500); + Console.WriteLine("Hello World!"); + } + } + + private static void Go() + { + Count = 0; + testList = new List(); + + var newConfig = new IdGeneratorOptions() + { + Method = 1, + StartTime = DateTime.Now.AddYears(-1), + + //TopOverCostCount = 1000, + WorkerIdBitLength = 6, + SeqBitLength = 6, + + //MinSeqNumber = 11, + //MaxSeqNumber = 200, + }; + + // ++++++++++++++++++++++++++++++++ + if (single) + { + newConfig.WorkerId = 1; + IdGeneratorOptions options1 = (newConfig); + if (IdGen == null) + { + IdGen = new YitIdGenerator(options1); + } + + if (outputLog) + { + IdGen.GenIdActionAsync = (arg => + { + if (arg.ActionType == 1) + { + Console.WriteLine($">>>> {arg.WorkerId}:开始:{DateTime.Now.ToString("mm:ss:fff")}, 周期次序:{arg.TermIndex}"); + } + else if (arg.ActionType == 2) + { + Console.WriteLine($"<<<< {arg.WorkerId}:结束:{DateTime.Now.ToString("mm:ss:fff")},漂移 {arg.OverCostCountInOneTerm} 次,产生 {arg.GenCountInOneTerm} 个, 周期次序:{arg.TermIndex}"); + } + if (arg.ActionType == 8) + { + Console.WriteLine($"---- {arg.WorkerId}:AA结束:{DateTime.Now.ToString("mm:ss:fff")},时间回拨"); + } + }); + } + + for (int i = 1; i < workerCount + 1; i++) + { + Console.WriteLine("Gen:" + i); + var test = new GenTest(IdGen, genIdCount, i); + testList.Add(test); + test.GenId(); + } + } + else + { + for (int i = 1; i < workerCount + 1; i++) + { + IdGeneratorOptions options = + new IdGeneratorOptions() + { + WorkerId = (ushort)i, // workerId 不能设置为0 + WorkerIdBitLength = newConfig.WorkerIdBitLength, + SeqBitLength = newConfig.SeqBitLength, + MinSeqNumber = newConfig.MinSeqNumber, + MaxSeqNumber = newConfig.MaxSeqNumber, + Method = newConfig.Method, + }; + + Console.WriteLine("Gen:" + i); + var idGen2 = new YitIdGenerator(options); + var test = new GenTest(idGen2, genIdCount, i); + + if (outputLog) + { + idGen2.GenIdActionAsync = (arg => + { + Console.WriteLine($"{DateTime.Now.ToString("mm:ss:fff")} {arg.WorkerId} 漂移了 {arg.OverCostCountInOneTerm}, 顺序:{arg.TermIndex}"); + }); + } + + testList.Add(test); + test.GenId(); + } + } + + while (Count < workerCount) + { + //Console.WriteLine("Count:" + Count); + Thread.Sleep(1000); + } + //Console.WriteLine("Count:" + Count); + + if (!checkResult) + { + return; + } + + var dupCount = 0; + foreach (var id in testList[0].idList) + { + if (id == 0) + { + Console.WriteLine("############### 错误的ID:" + id + "###########"); + } + + for (int j = 1; j < testList.Count; j++) + { + if (testList[j].idList.Contains(id)) + { + dupCount++; + Console.WriteLine("xxxxxxxxxx 重复的ID:" + id); + } + } + } + + if (dupCount > 0) + { + Console.WriteLine($"重复数量:{dupCount}"); + } + + } + + } +} diff --git a/src/Yitter.IdGenTest/Yitter.IdGenTest.csproj b/src/Yitter.IdGenTest/Yitter.IdGenTest.csproj new file mode 100644 index 0000000000000000000000000000000000000000..f1a608ada7696417cbd1b18732127de8b67142ec --- /dev/null +++ b/src/Yitter.IdGenTest/Yitter.IdGenTest.csproj @@ -0,0 +1,16 @@ + + + + Exe + net5.0 + + + + latest + + + + + + + diff --git a/src/Yitter.IdGenerator.sln b/src/Yitter.IdGenerator.sln new file mode 100644 index 0000000000000000000000000000000000000000..2cbc6eac6415d2f8a64d3447fc9d073efd38d57b --- /dev/null +++ b/src/Yitter.IdGenerator.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31005.135 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yitter.IdGenerator", "Yitter.IdGenerator\Yitter.IdGenerator.csproj", "{FF8DAF11-34E7-4842-ADF2-3722A1A5FBB2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yitter.IdGenTest", "Yitter.IdGenTest\Yitter.IdGenTest.csproj", "{67426F7D-0A3B-4645-B4D7-5487215D3E2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FF8DAF11-34E7-4842-ADF2-3722A1A5FBB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF8DAF11-34E7-4842-ADF2-3722A1A5FBB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF8DAF11-34E7-4842-ADF2-3722A1A5FBB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF8DAF11-34E7-4842-ADF2-3722A1A5FBB2}.Release|Any CPU.Build.0 = Release|Any CPU + {67426F7D-0A3B-4645-B4D7-5487215D3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67426F7D-0A3B-4645-B4D7-5487215D3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67426F7D-0A3B-4645-B4D7-5487215D3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67426F7D-0A3B-4645-B4D7-5487215D3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5C87B69B-CE8D-411F-AFAF-298C7BC7C2EA} + EndGlobalSection +EndGlobal diff --git a/src/Yitter.IdGenerator/IIdGenerator.cs b/src/Yitter.IdGenerator/IIdGenerator.cs new file mode 100644 index 0000000000000000000000000000000000000000..29bc25dc460b7421e01aa43a83fa11908c0ef222 --- /dev/null +++ b/src/Yitter.IdGenerator/IIdGenerator.cs @@ -0,0 +1,11 @@ +using System; + +namespace Yitter.IdGenerator +{ + public interface IIdGenerator + { + Action GenIdActionAsync { get; set; } + + long NewLong(); + } +} diff --git a/src/Yitter.IdGenerator/ISnowWorker.cs b/src/Yitter.IdGenerator/ISnowWorker.cs new file mode 100644 index 0000000000000000000000000000000000000000..361d4571869bbf6a64673efd91dc67013fe14edf --- /dev/null +++ b/src/Yitter.IdGenerator/ISnowWorker.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using static Yitter.IdGenerator.IIdGenerator; + +namespace Yitter.IdGenerator +{ + internal interface ISnowWorker + { + Action GenAction { get; set; } + + long NextId(); + } +} diff --git a/src/Yitter.IdGenerator/IdGeneratorOptions.cs b/src/Yitter.IdGenerator/IdGeneratorOptions.cs new file mode 100644 index 0000000000000000000000000000000000000000..46411775ad17515f573593b37acab1e6887f216b --- /dev/null +++ b/src/Yitter.IdGenerator/IdGeneratorOptions.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Yitter.IdGenerator +{ + public class IdGeneratorOptions + { + /// + /// 雪花计算方法 + /// (1|2) + /// + public short Method { get; set; } = 1; + + /// + /// 开始时间(UTC格式) + /// 不能超过当前系统时间 + /// + public DateTime StartTime { get; set; } = DateTime.MinValue; + + /// + /// 机器码 + /// 与 WorkerIdBitLength 有关系 + /// + public ushort WorkerId { get; set; } = 0; + + /// + /// 机器码位长 + /// 范围:2-21(要求:序列数位长+机器码位长不超过22)。 + /// 建议范围:6-12。 + /// + public byte WorkerIdBitLength { get; set; } = 6;//10; + + /// + /// 序列数位长 + /// 范围:2-21(要求:序列数位长+机器码位长不超过22)。 + /// 建议范围:6-14。 + /// + public byte SeqBitLength { get; set; } = 6;//10; + + /// + /// 最大序列数(含) + /// (由SeqBitLength计算的最大值) + /// + public int MaxSeqNumber { get; set; } = 0; + + /// + /// 最小序列数(含) + /// 默认11,不小于5,不大于MaxSeqNumber-2 + /// + public ushort MinSeqNumber { get; set; } = 11; + + /// + /// 最大漂移次数(含), + /// 默认2000,推荐范围500-10000(与计算能力有关) + /// + public int TopOverCostCount { get; set; } = 2000; + + + public IdGeneratorOptions() + { + + } + } +} diff --git a/src/Yitter.IdGenerator/OverCostActionArg.cs b/src/Yitter.IdGenerator/OverCostActionArg.cs new file mode 100644 index 0000000000000000000000000000000000000000..22b955012155b2083cf52dc1173683e088e932c2 --- /dev/null +++ b/src/Yitter.IdGenerator/OverCostActionArg.cs @@ -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; + } + } +} diff --git a/src/Yitter.IdGenerator/SnowWorkerM1.cs b/src/Yitter.IdGenerator/SnowWorkerM1.cs new file mode 100644 index 0000000000000000000000000000000000000000..ec9446b0b004a3031d298b406b8dc1e86fcdd20e --- /dev/null +++ b/src/Yitter.IdGenerator/SnowWorkerM1.cs @@ -0,0 +1,297 @@ +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(() => + { + if (arg.ActionType == 2 && _TermIndex > 10000) + { + _TermIndex = 0; + } + + 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 (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 = 0; + + 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(); + } + } + } +} diff --git a/src/Yitter.IdGenerator/SnowWorkerM2.cs b/src/Yitter.IdGenerator/SnowWorkerM2.cs new file mode 100644 index 0000000000000000000000000000000000000000..c660c72c80ccf2860c44451d26bf13bbb29c318b --- /dev/null +++ b/src/Yitter.IdGenerator/SnowWorkerM2.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Yitter.IdGenerator +{ + /// + /// 常规雪花算法 + /// + 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; + } + } + + } +} diff --git a/src/Yitter.IdGenerator/YitIdGenerator.cs b/src/Yitter.IdGenerator/YitIdGenerator.cs new file mode 100644 index 0000000000000000000000000000000000000000..f67ba5da6bcf4b2245f36649538648a13aa4ee05 --- /dev/null +++ b/src/Yitter.IdGenerator/YitIdGenerator.cs @@ -0,0 +1,84 @@ +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 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("不满足条件:WorkerIdBitLength + 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(); + } + } +} diff --git a/src/Yitter.IdGenerator/Yitter.IdGenerator.csproj b/src/Yitter.IdGenerator/Yitter.IdGenerator.csproj new file mode 100644 index 0000000000000000000000000000000000000000..ef2d4c2ada4a0743c5ae747b933a4ccc99b20334 --- /dev/null +++ b/src/Yitter.IdGenerator/Yitter.IdGenerator.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + + + + latest + + + + 5 + + + + + +