فهرست منبع

分布式自增ID算法

懒得勤快 6 سال پیش
والد
کامیت
8d8c1b221d

+ 1 - 1
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>netcoreapp2.1</TargetFramework>
-    <Version>2.1.5.2</Version>
+    <Version>2.1.6</Version>
     <Authors>懒得勤快</Authors>
     <Company>masuit.com</Company>
     <Description>包含一些常用的操作类,大都是静态类,加密解密,反射操作,硬件信息,字符串扩展方法,日期时间扩展操作,大文件拷贝,图像裁剪,html处理,验证码、NoSql等常用封装。

+ 172 - 0
Masuit.Tools.Core/Systems/SnowFlake.cs

@@ -0,0 +1,172 @@
+using System;
+using Masuit.Tools.Strings;
+
+namespace Masuit.Tools.Systems
+{
+    /// <summary>
+    /// 动态生产有规律的分布式ID
+    /// </summary>
+    public class SnowFlake
+    {
+        #region 私有字段
+
+        private static long _machineId; //机器码
+        private static long _datacenterId; //数据ID
+        private static long _sequence; //计数从零开始
+
+        private const long Twepoch = 687888001020L; //唯一时间随机量
+
+        private const long MachineIdBits = 5L; //机器码字节数
+        private const long DatacenterIdBits = 5L; //数据字节数
+        private const long MaxMachineId = -1L ^ -1L << (int)MachineIdBits; //最大机器码
+        private const long MaxDatacenterId = -1L ^ (-1L << (int)DatacenterIdBits); //最大数据ID
+
+        private const long SequenceBits = 12L; //计数器字节数,12个字节用来保存计数码        
+        private const long MachineIdShift = SequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
+        private const long DatacenterIdShift = SequenceBits + MachineIdBits;
+        private const long TimestampLeftShift = DatacenterIdShift + DatacenterIdBits; //时间戳左移动位数就是机器码+计数器总字节数+数据字节数
+        private const long SequenceMask = -1L ^ -1L << (int)SequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微秒在进行生成
+        private static long _lastTimestamp = -1L; //最后时间戳
+
+        private static readonly object SyncRoot = new object(); //加锁对象
+        private static readonly NumberFormater NumberFormater = new NumberFormater(36);
+        private static SnowFlake _snowFlake;
+
+        #endregion
+
+        public static SnowFlake GetInstance()
+        {
+            return _snowFlake ?? (_snowFlake = new SnowFlake());
+        }
+
+        public SnowFlake()
+        {
+            Snowflakes(0L, -1);
+        }
+
+        public SnowFlake(long machineId)
+        {
+            Snowflakes(machineId, -1);
+        }
+
+        public SnowFlake(long machineId, long datacenterId)
+        {
+            Snowflakes(machineId, datacenterId);
+        }
+
+        private void Snowflakes(long machineId, long datacenterId)
+        {
+            if (machineId >= 0)
+            {
+                if (machineId > MaxMachineId)
+                {
+                    throw new Exception("机器码ID非法");
+                }
+
+                _machineId = machineId;
+            }
+
+            if (datacenterId >= 0)
+            {
+                if (datacenterId > MaxDatacenterId)
+                {
+                    throw new Exception("数据中心ID非法");
+                }
+
+                _datacenterId = datacenterId;
+            }
+        }
+
+        /// <summary>
+        /// 生成当前时间戳
+        /// </summary>
+        /// <returns>毫秒</returns>
+        private static long GetTimestamp()
+        {
+            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
+        }
+
+        /// <summary>
+        /// 获取下一微秒时间戳
+        /// </summary>
+        /// <param name="lastTimestamp"></param>
+        /// <returns></returns>
+        private static long GetNextTimestamp(long lastTimestamp)
+        {
+            long timestamp = GetTimestamp();
+            if (timestamp <= lastTimestamp)
+            {
+                timestamp = GetTimestamp();
+            }
+
+            return timestamp;
+        }
+
+        /// <summary>
+        /// 获取长整形的ID
+        /// </summary>
+        /// <returns></returns>
+        public long GetLongId()
+        {
+            lock (SyncRoot)
+            {
+                long timestamp = GetTimestamp();
+                if (_lastTimestamp == timestamp)
+                {
+                    //同一微秒中生成ID
+                    _sequence = (_sequence + 1) & SequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
+                    if (_sequence == 0)
+                    {
+                        //一微秒内产生的ID计数已达上限,等待下一微秒
+                        timestamp = GetNextTimestamp(_lastTimestamp);
+                    }
+                }
+                else
+                {
+                    //不同微秒生成ID
+                    _sequence = 0L;
+                }
+
+                if (timestamp < _lastTimestamp)
+                {
+                    throw new Exception("时间戳比上一次生成ID时时间戳还小,故异常");
+                }
+
+                _lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
+                long id = ((timestamp - Twepoch) << (int)TimestampLeftShift) | (_datacenterId << (int)DatacenterIdShift) | (_machineId << (int)MachineIdShift) | _sequence;
+                return id;
+            }
+        }
+
+        /// <summary>
+        /// 获取一个字符串表示形式的id
+        /// </summary>
+        /// <returns></returns>
+        public string GetUniqueId()
+        {
+            return NumberFormater.ToString(GetLongId());
+        }
+
+        /// <summary>
+        /// 获取一个字符串表示形式的id
+        /// </summary>
+        /// <param name="maxLength">最大长度,至少6位</param>
+        /// <returns></returns>
+        public string GetUniqueShortId(int maxLength = 8)
+        {
+            if (maxLength < 6)
+            {
+                throw new ArgumentException("最大长度至少需要6位");
+            }
+
+            string id = GetUniqueId();
+            int index = id.Length - maxLength;
+            if (index < 0)
+            {
+                index = 0;
+            }
+
+            return id.Substring(index);
+        }
+    }
+}

+ 1 - 0
Masuit.Tools/Masuit.Tools.csproj

@@ -129,6 +129,7 @@
     <Compile Include="Systems\EnumExt.cs" />
     <Compile Include="Systems\Lock.cs" />
     <Compile Include="Systems\RedisLock.cs" />
+    <Compile Include="Systems\SnowFlake.cs" />
     <Compile Include="Systems\StopwatchExtension.cs" />
     <Compile Include="Validator\ComplexPassword.cs" />
     <Compile Include="Validator\IsEmailAttribute.cs" />

+ 2 - 2
Masuit.Tools/Properties/AssemblyInfo.cs

@@ -36,7 +36,7 @@ using System.Runtime.InteropServices;
 // 方法是按如下所示使用“*”: :
 // [assembly: AssemblyVersion("1.0.*")]
 
-[assembly: AssemblyVersion("2.1.5.2")]
-[assembly: AssemblyFileVersion("2.1.5.2")]
+[assembly: AssemblyVersion("2.1.6.0")]
+[assembly: AssemblyFileVersion("2.1.6.0")]
 [assembly: NeutralResourcesLanguage("zh-CN")]
 

+ 172 - 0
Masuit.Tools/Systems/SnowFlake.cs

@@ -0,0 +1,172 @@
+using System;
+using Masuit.Tools.Strings;
+
+namespace Masuit.Tools.Systems
+{
+    /// <summary>
+    /// 动态生产有规律的分布式ID
+    /// </summary>
+    public class SnowFlake
+    {
+        #region 私有字段
+
+        private static long _machineId; //机器码
+        private static long _datacenterId; //数据ID
+        private static long _sequence; //计数从零开始
+
+        private const long Twepoch = 687888001020L; //唯一时间随机量
+
+        private const long MachineIdBits = 5L; //机器码字节数
+        private const long DatacenterIdBits = 5L; //数据字节数
+        private const long MaxMachineId = -1L ^ -1L << (int)MachineIdBits; //最大机器码
+        private const long MaxDatacenterId = -1L ^ (-1L << (int)DatacenterIdBits); //最大数据ID
+
+        private const long SequenceBits = 12L; //计数器字节数,12个字节用来保存计数码        
+        private const long MachineIdShift = SequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
+        private const long DatacenterIdShift = SequenceBits + MachineIdBits;
+        private const long TimestampLeftShift = DatacenterIdShift + DatacenterIdBits; //时间戳左移动位数就是机器码+计数器总字节数+数据字节数
+        private const long SequenceMask = -1L ^ -1L << (int)SequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微秒在进行生成
+        private static long _lastTimestamp = -1L; //最后时间戳
+
+        private static readonly object SyncRoot = new object(); //加锁对象
+        private static readonly NumberFormater NumberFormater = new NumberFormater(36);
+        private static SnowFlake _snowFlake;
+
+        #endregion
+
+        public static SnowFlake GetInstance()
+        {
+            return _snowFlake ?? (_snowFlake = new SnowFlake());
+        }
+
+        public SnowFlake()
+        {
+            Snowflakes(0L, -1);
+        }
+
+        public SnowFlake(long machineId)
+        {
+            Snowflakes(machineId, -1);
+        }
+
+        public SnowFlake(long machineId, long datacenterId)
+        {
+            Snowflakes(machineId, datacenterId);
+        }
+
+        private void Snowflakes(long machineId, long datacenterId)
+        {
+            if (machineId >= 0)
+            {
+                if (machineId > MaxMachineId)
+                {
+                    throw new Exception("机器码ID非法");
+                }
+
+                _machineId = machineId;
+            }
+
+            if (datacenterId >= 0)
+            {
+                if (datacenterId > MaxDatacenterId)
+                {
+                    throw new Exception("数据中心ID非法");
+                }
+
+                _datacenterId = datacenterId;
+            }
+        }
+
+        /// <summary>
+        /// 生成当前时间戳
+        /// </summary>
+        /// <returns>毫秒</returns>
+        private static long GetTimestamp()
+        {
+            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
+        }
+
+        /// <summary>
+        /// 获取下一微秒时间戳
+        /// </summary>
+        /// <param name="lastTimestamp"></param>
+        /// <returns></returns>
+        private static long GetNextTimestamp(long lastTimestamp)
+        {
+            long timestamp = GetTimestamp();
+            if (timestamp <= lastTimestamp)
+            {
+                timestamp = GetTimestamp();
+            }
+
+            return timestamp;
+        }
+
+        /// <summary>
+        /// 获取长整形的ID
+        /// </summary>
+        /// <returns></returns>
+        public long GetLongId()
+        {
+            lock (SyncRoot)
+            {
+                long timestamp = GetTimestamp();
+                if (_lastTimestamp == timestamp)
+                {
+                    //同一微秒中生成ID
+                    _sequence = (_sequence + 1) & SequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
+                    if (_sequence == 0)
+                    {
+                        //一微秒内产生的ID计数已达上限,等待下一微秒
+                        timestamp = GetNextTimestamp(_lastTimestamp);
+                    }
+                }
+                else
+                {
+                    //不同微秒生成ID
+                    _sequence = 0L;
+                }
+
+                if (timestamp < _lastTimestamp)
+                {
+                    throw new Exception("时间戳比上一次生成ID时时间戳还小,故异常");
+                }
+
+                _lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
+                long id = ((timestamp - Twepoch) << (int)TimestampLeftShift) | (_datacenterId << (int)DatacenterIdShift) | (_machineId << (int)MachineIdShift) | _sequence;
+                return id;
+            }
+        }
+
+        /// <summary>
+        /// 获取一个字符串表示形式的id
+        /// </summary>
+        /// <returns></returns>
+        public string GetUniqueId()
+        {
+            return NumberFormater.ToString(GetLongId());
+        }
+
+        /// <summary>
+        /// 获取一个字符串表示形式的id
+        /// </summary>
+        /// <param name="maxLength">最大长度,至少6位</param>
+        /// <returns></returns>
+        public string GetUniqueShortId(int maxLength = 8)
+        {
+            if (maxLength < 6)
+            {
+                throw new ArgumentException("最大长度至少需要6位");
+            }
+
+            string id = GetUniqueId();
+            int index = id.Length - maxLength;
+            if (index < 0)
+            {
+                index = 0;
+            }
+
+            return id.Substring(index);
+        }
+    }
+}