• 【NET】雪花算法


    一、.NET 版本

    using System;
    
    namespace Snowflake
    {
        class Program
        {
            private static readonly IdWorker Worker = new IdWorker(1, 1); // 大并发的情况下,减少new的次数可以有效避免重复的可能
    
            static void Main(string[] args)
            {
                // var worker = new IdWorker(1, 1);
                long id = Worker.NextId();
                Console.WriteLine($"生成的ID为:{id},他的长度是:{id.ToString().Length}");
                Console.ReadKey();
            }
        }
    
        public class IdWorker
        {
            //基准时间
            public const long Twepoch = 1288834974657L;
            //机器标识位数
            const int WorkerIdBits = 5;
            //数据标志位数
            const int DatacenterIdBits = 5;
            //序列号识位数
            const int SequenceBits = 12;
            //机器ID最大值
            const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
            //数据标志ID最大值
            const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
            //序列号ID最大值
            private const long SequenceMask = -1L ^ (-1L << SequenceBits);
            //机器ID偏左移12位
            private const int WorkerIdShift = SequenceBits;
            //数据ID偏左移17位
            private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
            //时间毫秒左移22位
            public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
    
            private long _sequence = 0L;
            private long _lastTimestamp = -1L;
    
            public long WorkerId { get; protected set; }
            public long DatacenterId { get; protected set; }
            public long Sequence
            {
                get { return _sequence; }
                internal set { _sequence = value; }
            }
    
            public IdWorker(long workerId, long datacenterId, long sequence = 0L)
            {
                // 如果超出范围就抛出异常
                if (workerId > MaxWorkerId || workerId < 0)
                {
                    throw new ArgumentException(string.Format("worker Id 必须大于0,且不能大于MaxWorkerId: {0}", MaxWorkerId));
                }
    
                if (datacenterId > MaxDatacenterId || datacenterId < 0)
                {
                    throw new ArgumentException(string.Format("region Id 必须大于0,且不能大于MaxWorkerId: {0}", MaxDatacenterId));
                }
    
                //先检验再赋值
                WorkerId = workerId;
                DatacenterId = datacenterId;
                _sequence = sequence;
            }
    
            readonly object _lock = new Object();
            public virtual long NextId()
            {
                lock (_lock)
                {
                    var timestamp = TimeGen();
                    if (timestamp < _lastTimestamp)
                    {
                        throw new Exception(string.Format("时间戳必须大于上一次生成ID的时间戳.  拒绝为{0}毫秒生成id", _lastTimestamp - timestamp));
                    }
    
                    // 如果上次生成时间和当前时间相同,在同一毫秒内
                    if (_lastTimestamp == timestamp)
                    {
                        // sequence自增,和sequenceMask相与一下,去掉高位
                        _sequence = (_sequence + 1) & SequenceMask;
                        // 判断是否溢出,也就是每毫秒内超过1024,当为1024时,与sequenceMask相与,sequence就等于0
                        if (_sequence == 0)
                        {
                            // 等待到下一毫秒
                            timestamp = TilNextMillis(_lastTimestamp);
                        }
                    }
                    else
                    {
                        // 如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加,
                        // 为了保证尾数随机性更大一些,最后一位可以设置一个随机数
                        _sequence = 0;//new Random().Next(10);
                    }
    
                    _lastTimestamp = timestamp;
                    return ((timestamp - Twepoch) << TimestampLeftShift) | (DatacenterId << DatacenterIdShift) | (WorkerId << WorkerIdShift) | _sequence;
                }
            }
    
            // 防止产生的时间比之前的时间还要小(由于NTP回拨等问题),保持增量的趋势.
            protected virtual long TilNextMillis(long lastTimestamp)
            {
                var timestamp = TimeGen();
                while (timestamp <= lastTimestamp)
                {
                    timestamp = TimeGen();
                }
                return timestamp;
            }
    
            // 获取当前的时间戳
            protected virtual long TimeGen()
            {
                return TimeExtensions.CurrentTimeMillis();
            }
        }
    
        public static class TimeExtensions
        {
            public static Func<long> currentTimeFunc = InternalCurrentTimeMillis;
    
            public static long CurrentTimeMillis()
            {
                return currentTimeFunc();
            }
    
            public static IDisposable StubCurrentTime(Func<long> func)
            {
                currentTimeFunc = func;
                return new DisposableAction(() =>
                {
                    currentTimeFunc = InternalCurrentTimeMillis;
                });
            }
    
            public static IDisposable StubCurrentTime(long millis)
            {
                currentTimeFunc = () => millis;
                return new DisposableAction(() =>
                {
                    currentTimeFunc = InternalCurrentTimeMillis;
                });
            }
    
            private static readonly DateTime Jan1st1970 = new DateTime
                (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    
            private static long InternalCurrentTimeMillis()
            {
                return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
            }
        }
    
        public class DisposableAction : IDisposable
        {
            readonly Action _action;
    
            public DisposableAction(Action action)
            {
                if (action == null)
                    throw new ArgumentNullException("action");
                _action = action;
            }
    
            public void Dispose()
            {
                _action();
            }
        }
    }

    源码:https://github.com/dunitian/snowflake-net

    博客:https://www.cnblogs.com/dunitian/p/6130543.html

  • 相关阅读:
    Java8新特性之lambda表达式
    查询数据库存在特殊列字段的所有表的表名和字段名
    BigDecimal相关整理
    MyBatis正在爬的坑
    Java面试题整理
    qs库的使用
    配置proxy解决跨域问题
    PDF.js 使用方式
    HTML转义以及防止JS注入攻击
    jquery ajax跨域回调
  • 原文地址:https://www.cnblogs.com/chuankang/p/14084633.html
Copyright © 2020-2023  润新知