Commit c5b0b678 authored by Savorboard's avatar Savorboard

Add SnowflakeId generator class.

parent 86ae8bfb
// Copyright 2010-2012 Twitter, Inc.
// An object that generates IDs. This is broken into a separate class in case we ever want to support multiple worker threads per process
using System;
namespace DotNetCore.CAP.Infrastructure
{
public class SnowflakeId
{
public const long Twepoch = 1288834974657L;
private const int WorkerIdBits = 5;
private const int DatacenterIdBits = 5;
private const int SequenceBits = 12;
private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
private const int WorkerIdShift = SequenceBits;
private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
private const long SequenceMask = -1L ^ (-1L << SequenceBits);
private static SnowflakeId _snowflakeId;
private readonly object _lock = new object();
private long _lastTimestamp = -1L;
private SnowflakeId(long workerId, long datacenterId, long sequence = 0L)
{
WorkerId = workerId;
DatacenterId = datacenterId;
Sequence = sequence;
// sanity check for workerId
if (workerId > MaxWorkerId || workerId < 0)
throw new ArgumentException($"worker Id can't be greater than {MaxWorkerId} or less than 0");
if (datacenterId > MaxDatacenterId || datacenterId < 0)
throw new ArgumentException($"datacenter Id can't be greater than {MaxDatacenterId} or less than 0");
}
public long WorkerId { get; protected set; }
public long DatacenterId { get; protected set; }
public long Sequence { get; internal set; }
public static SnowflakeId Default(long datacenterId = 0)
{
return _snowflakeId ?? (_snowflakeId = new SnowflakeId(AppDomain.CurrentDomain.Id, datacenterId));
}
public virtual long NextId()
{
lock (_lock)
{
var timestamp = TimeGen();
if (timestamp < _lastTimestamp)
throw new Exception(
$"InvalidSystemClock: Clock moved backwards, Refusing to generate id for {_lastTimestamp - timestamp} milliseconds");
if (_lastTimestamp == timestamp)
{
Sequence = (Sequence + 1) & SequenceMask;
if (Sequence == 0) timestamp = TilNextMillis(_lastTimestamp);
}
else
{
Sequence = 0;
}
_lastTimestamp = timestamp;
var id = ((timestamp - Twepoch) << TimestampLeftShift) |
(DatacenterId << DatacenterIdShift) |
(WorkerId << WorkerIdShift) | Sequence;
return id;
}
}
protected virtual long TilNextMillis(long lastTimestamp)
{
var timestamp = TimeGen();
while (timestamp <= lastTimestamp) timestamp = TimeGen();
return timestamp;
}
protected virtual long TimeGen()
{
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment