using Amazon.Runtime.Internal.Util; using System.Net; using System.Runtime.InteropServices; namespace Business.Core.Utilities { /// /// 雪花算法生成id /// public class SnowFlake { /// /// 机器id所占的位数 /// private const int workerIdBits = 5; /// /// 数据表述id所占位数 /// private const int datacenterIdBits = 5; /// /// 支持的最大机器id,结果是31 /// private const long maxWorkerId = -1L ^ (-1L << workerIdBits); /// /// 支持的最大数据表示id,结果是31 /// private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /// /// 序列在id中所占的位数 /// private const int sequenceBits = 12; /// /// 数据标识id向左移17位 /// private const int datacenterIdShift = sequenceBits + workerIdBits; /// /// 机器ID向左移12位 /// private const int workerIdShift = sequenceBits; private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /// /// 生成序列的掩码 /// private const long sequenceMask = -1L ^ (-1L << sequenceBits); /// /// 数据中心ID(0-31) /// public long DatacenterId { get; private set; } /// /// 工作机器ID(0-31) /// public long WorkerId { get; private set; } /// /// 毫秒内序列(0-4095) /// public long Sequence { get; private set; } /// /// 上次生成ID的时间戳 /// public long LastTimestamp { get; private set; } /// /// 开始时间戳。首次使用前设置,否则无效,默认2010-1-1 /// public DateTime StartTimestamp { get; set; } = new DateTime(2010,1,1); static object syncRoot = new object(); static readonly Lazy snowflake = new(() => new SnowFlake()); /// /// 默认静态实例,WorkerId = 0,DatacenterId = 0 /// public static SnowFlake Instance { get; } = snowflake.Value; /// /// 构造函数 /// /// public SnowFlake() { //生成workerid和datacenterid SetWorkIdAndDatacenterId(); if (WorkerId > maxWorkerId || WorkerId < 0) { throw new ArgumentOutOfRangeException(nameof(WorkerId),$"不能大于{maxWorkerId}或小于0"); } if (DatacenterId > maxDatacenterId || DatacenterId < 0) { throw new ArgumentOutOfRangeException(nameof(DatacenterId), $"不能大于{maxDatacenterId}或小于0"); } Sequence = 0L; LastTimestamp = -1L; } /// /// 生成WorkerId和DatacenterId /// /// private void SetWorkIdAndDatacenterId() { string? ipAdress = ""; try { string? hostName; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { hostName = Environment.GetEnvironmentVariable("MY_NODE_NAME"); ipAdress = Environment.GetEnvironmentVariable("MY_HOST_IP"); } else { hostName = Dns.GetHostName(); IPHostEntry ipEntity = Dns.GetHostEntry(hostName); for (int i = 0; i < ipEntity.AddressList.Length; i++) { if (ipEntity.AddressList[i].AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { ipAdress = ipEntity.AddressList[i].ToString(); } } } if (string.IsNullOrEmpty(ipAdress) || string.IsNullOrEmpty(hostName)) { WorkerId = 0; DatacenterId = 0; } else { WorkerId = StringToLong(ipAdress); DatacenterId = StringToLong(hostName); } } catch (Exception) { WorkerId = 0; DatacenterId = 0; } } /// /// 生成workerid和datacenterid /// /// /// private long StringToLong(string strInput) { int[] arrays = ToCodePoints(strInput); int sum = 0; for (int i = 0; i < arrays.Length; i++) { sum += arrays[i]; } return (long)(sum % 32); } /// /// 将字符串转换成数组 /// /// /// private int[] ToCodePoints(string strInput) { var codePoints = new List(strInput.Length); for (int i = 0; i < strInput.Length; i++) { codePoints.Add(char.ConvertToUtf32(strInput, i)); if (char.IsHighSurrogate(strInput[i])) { i += 1; } } return codePoints.ToArray(); } /// /// 获取下一个ID /// /// public long NextId() { lock (syncRoot) { //获取当前时间戳 long timestamp = GetCurrentTimestamp(); if (timestamp > LastTimestamp)//时间戳改变,毫秒内序列重置 { Sequence = 0L; } else if (timestamp == LastTimestamp)//如果是同一时间生成的,则进行毫秒内序列 { Sequence = (Sequence + 1) & sequenceMask; if (Sequence == 0)//毫秒内序列溢出 { timestamp = GetNextTimestamp(LastTimestamp);//阻塞到下一个毫秒,获取新的时间戳 } } else//当前时间小于上一次ID生成的时间戳,证明系统时钟被回拨,此时需要做回拨处理 { Sequence = (Sequence + 1) & sequenceMask; if (Sequence > 0) { timestamp = LastTimestamp;//停留再最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题 } else { timestamp = LastTimestamp + 1;//直接进位到下一个毫秒 } } LastTimestamp = timestamp;//上次生成ID的时间戳 //移动并通过或运算拼到一起组成64位的ID var id = (timestamp << timestampLeftShift) | (DatacenterId << datacenterIdShift) | (WorkerId << workerIdShift) | Sequence; return id; } } /// /// 阻塞到下一个毫秒,直到获得新的时间戳 /// /// /// private long GetNextTimestamp(long lastTimestamp) { //获取当前时间戳 long timestamp = GetCurrentTimestamp(); while (timestamp <= lastTimestamp) { timestamp = GetCurrentTimestamp(); } return timestamp; } /// /// 获取当前时间戳 /// /// private long GetCurrentTimestamp() { return (long)(DateTime.Now - StartTimestamp).TotalMilliseconds; } } }