| @@ -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 | |||||
| @@ -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/) | |||||
| @@ -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 | |||||
| #### 软件架构 | |||||
| 软件架构说明 | |||||
| @@ -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 | |||||
| @@ -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<long> idList = new List<long>(); | |||||
| 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); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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<GenTest> testList = new List<GenTest>(); | |||||
| 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<GenTest>(); | |||||
| 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}"); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,16 @@ | |||||
| <Project Sdk="Microsoft.NET.Sdk"> | |||||
| <PropertyGroup> | |||||
| <OutputType>Exe</OutputType> | |||||
| <TargetFramework>net5.0</TargetFramework> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup> | |||||
| <LangVersion>latest</LangVersion> | |||||
| </PropertyGroup> | |||||
| <ItemGroup> | |||||
| <ProjectReference Include="..\Yitter.IdGenerator\Yitter.IdGenerator.csproj" /> | |||||
| </ItemGroup> | |||||
| </Project> | |||||
| @@ -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 | |||||
| @@ -0,0 +1,11 @@ | |||||
| using System; | |||||
| namespace Yitter.IdGenerator | |||||
| { | |||||
| public interface IIdGenerator | |||||
| { | |||||
| Action<OverCostActionArg> GenIdActionAsync { get; set; } | |||||
| long NewLong(); | |||||
| } | |||||
| } | |||||
| @@ -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<OverCostActionArg> GenAction { get; set; } | |||||
| long NextId(); | |||||
| } | |||||
| } | |||||
| @@ -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() | |||||
| { | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,297 @@ | |||||
| 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(() => | |||||
| { | |||||
| 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(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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<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("不满足条件: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(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| <Project Sdk="Microsoft.NET.Sdk"> | |||||
| <PropertyGroup> | |||||
| <TargetFramework>netstandard2.0</TargetFramework> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup> | |||||
| <LangVersion>latest</LangVersion> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |||||
| <WarningLevel>5</WarningLevel> | |||||
| </PropertyGroup> | |||||
| <ItemGroup> | |||||
| </ItemGroup> | |||||
| </Project> | |||||