Files
TouchSocket/src/TouchSocket.Core/Core/SnowflakeIDGenerator.cs
若汝棋茗 2798d3eb66 发布:3.1.0
2025-04-26 12:19:40 +08:00

135 lines
4.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页https://touchsocket.net/
// 交流QQ群234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
using System;
namespace TouchSocket.Core;
/// <summary>
/// 雪花Id生成器(该代码来自网络)
/// </summary>
public class SnowflakeIdGenerator
{
private const int SequenceBits = 10;
/// <summary>
/// 一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成
/// </summary>
private const long SequenceMask = -1L ^ -1L << SequenceBits;
private const int TimestampLeftShift = SequenceBits + WorkerIdBits;
private const int WorkerIdBits = 4;
//计数器字节数10个字节用来保存计数码
private const int WorkerIdShift = SequenceBits;
private static long s_sequence = 0L;
//机器Id
private static long s_workerId;
private readonly long m_twepoch = 687888001020L;
private long m_lastTimestamp = -1L;
static SnowflakeIdGenerator()
{
}
/// <summary>
/// 初始化 SnowflakeIdGenerator 类的新实例。
/// </summary>
/// <param name="workerId">工作机器的唯一标识符,用于区分不同的工作机器。</param>
/// <exception cref="Exception">如果 workerId 大于最大工作机器ID或小于0则抛出异常。</exception>
public SnowflakeIdGenerator(long workerId)
{
// 检查 workerId 是否在有效范围内,如果不在,则抛出异常
if (workerId > MaxWorkerId || workerId < 0)
{
throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0 ", MaxWorkerId));
}
// 设置工作机器ID
s_workerId = workerId;
// 设置时间戳基准值这是Snowflake算法中用到的一个重要参数
this.m_twepoch = DateTimeOffset.UtcNow.Ticks - 10000;
}
/// <summary>
/// 最大机器Id
/// </summary>
public static long MaxWorkerId { get; private set; } = -1L ^ (-1L << WorkerIdBits); //最大机器Id
//唯一时间,这是一个避免重复的随机量,自行设定不要大于当前时间戳
//机器码字节数。4个字节用来保存机器码(定义为Long类型会出现最大偏移64位所以左移64位没有意义)
//机器码数据左移位数,就是后面计数器占用的位数
//时间戳左移动位数就是机器码和计数器总字节数
/// <summary>
/// 获取Id
/// </summary>
/// <returns></returns>
public long NextId()
{
lock (this)
{
var timestamp = this.TimeGen();
if (this.m_lastTimestamp == timestamp)
{ //同一微妙中生成Id
s_sequence = (s_sequence + 1) & SequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
if (s_sequence == 0)
{
//一微妙内产生的Id计数已达上限等待下一微妙
timestamp = this.TillNextMillis(this.m_lastTimestamp);
}
}
else
{ //不同微秒生成Id
s_sequence = 0; //计数清0
}
if (timestamp < this.m_lastTimestamp)
{ //如果当前时间戳比上一次生成Id时时间戳还小抛出异常因为不能保证现在生成的Id之前没有生成过
throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds",
this.m_lastTimestamp - timestamp));
}
this.m_lastTimestamp = timestamp; //把当前时间戳保存为最后生成Id的时间戳
var nextId = (timestamp - this.m_twepoch << TimestampLeftShift) | s_workerId << WorkerIdShift | s_sequence;
return nextId;
}
}
/// <summary>
/// 获取下一微秒时间戳
/// </summary>
/// <param name="lastTimestamp"></param>
/// <returns></returns>
private long TillNextMillis(long lastTimestamp)
{
var timestamp = this.TimeGen();
while (timestamp <= lastTimestamp)
{
timestamp = this.TimeGen();
}
return timestamp;
}
/// <summary>
/// 生成当前时间戳
/// </summary>
/// <returns></returns>
private long TimeGen()
{
return Environment.TickCount;
}
}