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;
}
}
}