init
This commit is contained in:
258
.gitignore
vendored
Normal file
258
.gitignore
vendored
Normal file
@@ -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
|
||||
37
README.en.md
Normal file
37
README.en.md
Normal file
@@ -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/)
|
||||
17
README.md
Normal file
17
README.md
Normal file
@@ -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
|
||||
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
|
||||
37
auto_commit.bat
Normal file
37
auto_commit.bat
Normal file
@@ -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
|
||||
49
src/Yitter.IdGenTest/GenTest.cs
Normal file
49
src/Yitter.IdGenTest/GenTest.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
156
src/Yitter.IdGenTest/Program.cs
Normal file
156
src/Yitter.IdGenTest/Program.cs
Normal file
@@ -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}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
16
src/Yitter.IdGenTest/Yitter.IdGenTest.csproj
Normal file
16
src/Yitter.IdGenTest/Yitter.IdGenTest.csproj
Normal file
@@ -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>
|
||||
31
src/Yitter.IdGenerator.sln
Normal file
31
src/Yitter.IdGenerator.sln
Normal file
@@ -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
|
||||
11
src/Yitter.IdGenerator/IIdGenerator.cs
Normal file
11
src/Yitter.IdGenerator/IIdGenerator.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Yitter.IdGenerator
|
||||
{
|
||||
public interface IIdGenerator
|
||||
{
|
||||
Action<OverCostActionArg> GenIdActionAsync { get; set; }
|
||||
|
||||
long NewLong();
|
||||
}
|
||||
}
|
||||
14
src/Yitter.IdGenerator/ISnowWorker.cs
Normal file
14
src/Yitter.IdGenerator/ISnowWorker.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
65
src/Yitter.IdGenerator/IdGeneratorOptions.cs
Normal file
65
src/Yitter.IdGenerator/IdGeneratorOptions.cs
Normal 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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/Yitter.IdGenerator/OverCostActionArg.cs
Normal file
26
src/Yitter.IdGenerator/OverCostActionArg.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
297
src/Yitter.IdGenerator/SnowWorkerM1.cs
Normal file
297
src/Yitter.IdGenerator/SnowWorkerM1.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/Yitter.IdGenerator/SnowWorkerM2.cs
Normal file
49
src/Yitter.IdGenerator/SnowWorkerM2.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
84
src/Yitter.IdGenerator/YitIdGenerator.cs
Normal file
84
src/Yitter.IdGenerator/YitIdGenerator.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/Yitter.IdGenerator/Yitter.IdGenerator.csproj
Normal file
18
src/Yitter.IdGenerator/Yitter.IdGenerator.csproj
Normal file
@@ -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>
|
||||
Reference in New Issue
Block a user