From e2fd7bb04fe60b6d5251c1f4ee7e605eaf7d734d Mon Sep 17 00:00:00 2001 From: sunkaixuna <610262374@qq.com> Date: Sat, 31 Jul 2021 20:19:19 +0800 Subject: [PATCH] Add Snowflake --- .../Snowflake/DisposableAction.cs | 21 ++++ .../DistributedSystem/Snowflake/IdWorker.cs | 118 ++++++++++++++++++ .../Snowflake/InvalidSystemClock.cs | 9 ++ .../Snowflake/SnowFlakeSingle.cs | 27 ++++ .../Snowflake/TimeExtensions.cs | 40 ++++++ Src/Asp.Net/SqlSugar/SqlSugar.csproj | 5 + 6 files changed, 220 insertions(+) create mode 100644 Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/DisposableAction.cs create mode 100644 Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/IdWorker.cs create mode 100644 Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/InvalidSystemClock.cs create mode 100644 Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/SnowFlakeSingle.cs create mode 100644 Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/TimeExtensions.cs diff --git a/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/DisposableAction.cs b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/DisposableAction.cs new file mode 100644 index 000000000..91fc608f2 --- /dev/null +++ b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/DisposableAction.cs @@ -0,0 +1,21 @@ +using System; + +namespace SqlSugar.DistributedSystem.Snowflake +{ + public class DisposableAction : IDisposable + { + readonly Action _action; + + public DisposableAction(Action action) + { + if (action == null) + throw new ArgumentNullException("action"); + _action = action; + } + + public void Dispose() + { + _action(); + } + } +} \ No newline at end of file diff --git a/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/IdWorker.cs b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/IdWorker.cs new file mode 100644 index 000000000..d53a87d23 --- /dev/null +++ b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/IdWorker.cs @@ -0,0 +1,118 @@ +/** Copyright 2010-2012 Twitter, Inc.*/ + +/** + * An object that generates IDs. + * This is broken into a separate class in case + * we ever want to support multiple worker threads + * per process + */ + +using System; + +namespace SqlSugar.DistributedSystem.Snowflake +{ + public class IdWorker + { + public const long Twepoch = 1288834974657L; + + const int WorkerIdBits = 5; + const int DatacenterIdBits = 5; + const int SequenceBits = 12; + const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); + const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); + + private const int WorkerIdShift = SequenceBits; + private const int DatacenterIdShift = SequenceBits + WorkerIdBits; + public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; + private const long SequenceMask = -1L ^ (-1L << SequenceBits); + + private long _sequence = 0L; + private long _lastTimestamp = -1L; + + + public IdWorker(long workerId, long datacenterId, long sequence = 0L) + { + WorkerId = workerId; + DatacenterId = datacenterId; + _sequence = sequence; + + // sanity check for workerId + if (workerId > MaxWorkerId || workerId < 0) + { + throw new ArgumentException( String.Format("worker Id can't be greater than {0} or less than 0", MaxWorkerId) ); + } + + if (datacenterId > MaxDatacenterId || datacenterId < 0) + { + throw new ArgumentException( String.Format("datacenter Id can't be greater than {0} or less than 0", MaxDatacenterId)); + } + + //log.info( + // String.Format("worker starting. timestamp left shift {0}, datacenter id bits {1}, worker id bits {2}, sequence bits {3}, workerid {4}", + // TimestampLeftShift, DatacenterIdBits, WorkerIdBits, SequenceBits, workerId) + // ); + } + + public long WorkerId {get; protected set;} + public long DatacenterId {get; protected set;} + + public long Sequence + { + get { return _sequence; } + internal set { _sequence = value; } + } + + // def get_timestamp() = System.currentTimeMillis + + readonly object _lock = new Object(); + + public virtual long NextId() + { + lock(_lock) + { + var timestamp = TimeGen(); + + if (timestamp < _lastTimestamp) + { + //exceptionCounter.incr(1); + //log.Error("clock is moving backwards. Rejecting requests until %d.", _lastTimestamp); + throw new InvalidSystemClock(String.Format( + "Clock moved backwards. Refusing to generate id for {0} milliseconds", _lastTimestamp - timestamp)); + } + + if (_lastTimestamp == timestamp) + { + _sequence = (_sequence + 1) & SequenceMask; + if (_sequence == 0) + { + timestamp = TilNextMillis(_lastTimestamp); + } + } else { + _sequence = 0; + } + + _lastTimestamp = timestamp; + var id = ((timestamp - Twepoch) << TimestampLeftShift) | + (DatacenterId << DatacenterIdShift) | + (WorkerId << WorkerIdShift) | _sequence; + + return id; + } + } + + protected virtual long TilNextMillis(long lastTimestamp) + { + var timestamp = TimeGen(); + while (timestamp <= lastTimestamp) + { + timestamp = TimeGen(); + } + return timestamp; + } + + protected virtual long TimeGen() + { + return System.CurrentTimeMillis(); + } + } +} \ No newline at end of file diff --git a/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/InvalidSystemClock.cs b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/InvalidSystemClock.cs new file mode 100644 index 000000000..a4d7cb70e --- /dev/null +++ b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/InvalidSystemClock.cs @@ -0,0 +1,9 @@ +using System; + +namespace SqlSugar.DistributedSystem.Snowflake +{ + public class InvalidSystemClock : Exception + { + public InvalidSystemClock(string message) : base(message) { } + } +} \ No newline at end of file diff --git a/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/SnowFlakeSingle.cs b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/SnowFlakeSingle.cs new file mode 100644 index 000000000..84fd706da --- /dev/null +++ b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/SnowFlakeSingle.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SqlSugar.DistributedSystem.Snowflake +{ + public sealed class SnowFlakeSingle + { + public static readonly SnowFlakeSingle instance = new SnowFlakeSingle(); + private SnowFlakeSingle() + { + worker = new Snowflake.IdWorker(1, 1); + } + static SnowFlakeSingle() { } + public static SnowFlakeSingle Instance + { + get { return instance; } + } + private Snowflake.IdWorker worker; + public long getID() + { + return worker.NextId(); + } + } +} diff --git a/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/TimeExtensions.cs b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/TimeExtensions.cs new file mode 100644 index 000000000..3b246b026 --- /dev/null +++ b/Src/Asp.Net/SqlSugar/DistributedSystem/Snowflake/TimeExtensions.cs @@ -0,0 +1,40 @@ +using System; + +namespace SqlSugar.DistributedSystem.Snowflake +{ + public static class System + { + public static Func currentTimeFunc = InternalCurrentTimeMillis; + + public static long CurrentTimeMillis() + { + return currentTimeFunc(); + } + + public static IDisposable StubCurrentTime(Func func) + { + currentTimeFunc = func; + return new DisposableAction(() => + { + currentTimeFunc = InternalCurrentTimeMillis; + }); + } + + public static IDisposable StubCurrentTime(long millis) + { + currentTimeFunc = () => millis; + return new DisposableAction(() => + { + currentTimeFunc = InternalCurrentTimeMillis; + }); + } + + private static readonly DateTime Jan1st1970 = new DateTime + (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + private static long InternalCurrentTimeMillis() + { + return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds; + } + } +} diff --git a/Src/Asp.Net/SqlSugar/SqlSugar.csproj b/Src/Asp.Net/SqlSugar/SqlSugar.csproj index 558114aa2..d1ea05a5e 100644 --- a/Src/Asp.Net/SqlSugar/SqlSugar.csproj +++ b/Src/Asp.Net/SqlSugar/SqlSugar.csproj @@ -90,6 +90,7 @@ + @@ -194,6 +195,10 @@ + + + +