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