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