[C#] 纯文本查看 复制代码
/// <summary>
/// 雪花算法
/// </summary>
public class Snowflake
{
private const long twepoch = 1675235380000L;//当前时间戳
private const int workerIdBits = 5; //机器id所占的位数
private const int datacenterIdBits = 5; //数据标识id所占的位数
private long maxWorkerId = -1L ^ (-1L << workerIdBits); //支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); //支持的最大数据标识id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private const int sequenceBits = 22 - workerIdBits - datacenterIdBits; //计数器字节数,12个字节用来保存计数码
private const int workerIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
private const int datacenterIdShift = sequenceBits + workerIdBits; //数据标识id向左移17位(12+5)
private int timestampLeftShift = datacenterIdShift + datacenterIdBits; //时间截向左移22位(5+5+12)
private long sequenceMask = -1L ^ (-1L << sequenceBits); //生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
private long workerId = 1; //工作机器ID(0~31)
private long datacenterId = 1; //数据中心ID(0~31)
private long sequence = 0L;//毫秒内序列(0~4095)
private long lastTimestamp = -1L; //上次生成ID的时间截
private readonly object lockObj = new object();
/// <summary>
/// 构造函数
/// </summary>
/// <param name="workerId">机器ID(0-31)</param>
/// <param name="datacenterId">数据中心ID(0-31)</param>
public Snowflake(int workerId, int datacenterId)
{
if (workerId > maxWorkerId || workerId < 0)
throw new Exception($"机器ID 不能超过{maxWorkerId}");
if (datacenterId > maxDatacenterId || datacenterId < 0)
throw new Exception($" 数据中心ID 不能超过{maxDatacenterId}");
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public long Nextval
{
get
{
lock (lockObj)
{
return nextId();
}
}
}
/// <summary>
/// 获取下一位ID
/// </summary>
private long nextId()
{
long timestamp = timeGen();
if (lastTimestamp == timestamp)
{
//同一微妙中生成ID
sequence = (sequence + 1) & sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
if (sequence == 0)
{
//一微妙内产生的ID计数已达上限,等待下一微妙
timestamp = tillNextMillis(lastTimestamp);
}
}
else
{
//不同微秒生成ID
sequence = 0; //计数清0
}
if (timestamp < lastTimestamp)
{
//如果当前时间戳比上一次生成ID时时间戳还小,抛出异常,因为不能保证现在生成的ID之前没有生成过
throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds",
lastTimestamp - timestamp));
}
lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
long nextId = ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
return nextId;
}
/// <summary>
/// 获取下一微秒时间戳
/// </summary>
/// <param name="lastTimestamp"></param>
/// <returns></returns>
private long tillNextMillis(long lastTimestamp)
{
long timestamp = timeGen();
while (timestamp <= lastTimestamp)
{
timestamp = timeGen();
}
return timestamp;
}
/// <summary>
/// 生成当前时间戳
/// </summary>
/// <returns></returns>
private long timeGen()
{
return ToTimestamp(DateTime.Now);
}
/// <summary>
/// 获取当前的时间戳, 返回13位/10位时间戳
/// </summary>
/// <param name="time"></param>
/// <param name="isGet10Bits">是否获取10位时间戳, 默认flase</param>
/// <returns></returns>
public long ToTimestamp(DateTime time, bool isGet10Bits = false)
{
long val = 0L;
try
{
TimeSpan ts = time - new DateTime(1970, 1, 1, 0, 0, 0, 0);
val = Convert.ToInt64(ts.TotalMilliseconds);
if (isGet10Bits)
val /= 1000;
}
catch (Exception ex)
{
throw;
}
return val;
}
}
|