基于.NET Standard的分布式自增ID算法--Snowflake代码实现

概述

上篇文章介绍了3种常见的Id生成算法,本篇主要介绍如何使用C#实现Snowflake。

基础字段

/// <summary>
/// 工作节点Id(长度为5位)
/// </summary>
public long WorkId{get;protected set;}

/// <summary>
/// 机房Id(长度为5位)
/// </summary>
public long DataCenterId{get;protected set;}

/// <summary>
/// 当前生成的Id
/// </summary>
public long CurrentId { get;protected set; }

基础方法

//获取当前时间的Unix时间戳
 private long TimeGen()
{
      return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}

//生成新时间戳(一定比上次生成的时间戳大)
 private long TilNextMillis(long lastTimestamp)
 {
      var timestamp = TimeGen();
      while (timestamp <= lastTimestamp)
      {
           timestamp = TimeGen();
      }
       return timestamp;
}

Id生成核心方法

        private const long TwEpoch = 1543765593681L;//2018-12-2 15:47:06 +00:00

        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;
        private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
        private const long SequenceMask = -1L ^ (-1L << SequenceBits);

        private long _sequence = 0L;
        private long _lastTimestamp = -1L;

        private readonly object _lock = new object();
        
         public long NextId()
        {
            lock (_lock)
            {
                var timestamp = TimeGen();
                if (timestamp < _lastTimestamp)
                {
                    //TODO 是否可以考虑直接等待?
                    throw new Exception(
                        $"Clock moved backwards or wrapped around. Refusing to generate id for {_lastTimestamp - timestamp} ticks");
                }

                if (_lastTimestamp == timestamp)
                {
                    _sequence = (_sequence + 1) & SequenceMask;
                    if (_sequence == 0)
                    {
                        timestamp = TilNextMillis(_lastTimestamp);
                    }
                }
                else
                {
                    _sequence = 0;
                }
                _lastTimestamp = timestamp;
                CurrentId = ((timestamp - TwEpoch) << TimestampLeftShift) |
                         (DatacenterId << DatacenterIdShift) |
                         (WorkerId << WorkerIdShift) | _sequence;

                return CurrentId;
            }
        }

完整实现、使用Demo以及benchmark测试请参见源代码:https://github.com/sampsonye/nice

posted @ 2018-12-09 16:20 蜗牛往前走 阅读(...) 评论(...) 编辑 收藏