| @@ -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> | |||