缩短版snowflake

发布于:2021-07-20 09:31

完整版最大生成18位long型数据,js最大安全整数是53个bit【9007199254740991】,服务端可以通过字段属性标记序列化返回前端时转string,新增修改相关表字段时还是不够方便(懒),而且99.999%系统哪用得上1024个机器节点,单台单秒并发409万6千的需求。(蓝星上能用的上这个需求的,不超过2位数)。

所以为了懒,简单调整了一下(调整后也轻松满足99%的系统需求了)。

/// <summary>

/// 缩短版,,起始2021-01-01 00:00:00的话,最大大概到2090-01-01 00:00:00。单台单秒并发12万8千,最多32台机器节点

/// </summary>

public class Snowflake

{

//初始时间截

private const long TwEpoch = 1609430400000L;//2021-01-01 00:00:00


private const int WorkerIdBits = 5; //机器id位宽

private const int SequenceBits = 7; //流水序列号位宽

private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); //31

private const int WorkerIdShift = SequenceBits;

private const int TimestampLeftShift = SequenceBits + WorkerIdBits; //12

private const long SequenceMask = -1L ^ (-1L << SequenceBits); //128 单毫秒并发能力

private long _sequence = 0L; //毫秒内序列,从0开始,最大128

private long _lastTimestamp = -1L; //上次时间戳

/// <summary>

///5bit机器节点 0-31台机器

/// </summary>

public long WorkerId { get; protected set; }

private readonly object _lock = new object();

/// <summary>

/// 基于Twitter的snowflake算法

/// </summary>

/// <param name="workerId">5bit数据机器</param>

/// <param name="sequence">初始序列</param>

public Snowflake(long workerId, long sequence = 0L)

{

WorkerId = workerId;

_sequence = sequence;

if (workerId > MaxWorkerId || workerId < 0)

{

throw new ArgumentException($"0<=机器节点<={MaxWorkerId}");

}

}


public long CurrentId { get; private set; }


/// <summary>

/// 获取下一个Id,该方法线程安全

/// </summary>

/// <returns></returns>

public long NextId()

{

lock (_lock)

{

//var timestamp = DateTimeHelper.GetUnixTimeStamp(DateTime.Now);

var timestamp = DateTimeHelper.GetUnixTimeStamp(new DateTime(2090,1,1));

if (timestamp < _lastTimestamp)

{

throw new Exception(

$"时钟回拨了. Refusing to generate id for {_lastTimestamp - timestamp} ticks");

}

//如果是同一时间生成的,则进行毫秒内序列

if (_lastTimestamp == timestamp)

{

_sequence = (_sequence + 1) & SequenceMask;

//sequence等于0说明毫秒内序列已经增长到最大值

if (_sequence == 0)

{

//阻塞到下一个毫秒,获得新的时间戳

timestamp = TilNextMillis(_lastTimestamp);

}

}

else //时间戳改变,毫秒内序列重置

{

_sequence = 0;

}

_lastTimestamp = timestamp;

//移位并通过或运算拼到一起组成64位的ID

CurrentId = ((timestamp - TwEpoch) << TimestampLeftShift) | (WorkerId << WorkerIdShift) | _sequence;


return CurrentId;

}

}

/// <summary>

/// 阻塞到下一个毫秒,直到获得新的时间戳

/// </summary>

/// <param name="lastTimestamp"></param>

/// <returns></returns>

private long TilNextMillis(long lastTimestamp)

{

var timestamp = DateTimeHelper.GetUnixTimeStamp(DateTime.Now);

while (timestamp <= lastTimestamp)

{

timestamp = DateTimeHelper.GetUnixTimeStamp(DateTime.Now);

}

return timestamp;

}

}