.NET ID生成器分享

  因为自增ID各种各样的原因,需要自己写一个ID的生成器,可以回溯时间,可以去重,可以分表分库等等。在我们封装的库中做了一个实现。

using System;

namespace Common
{
    /// <summary>
    /// id生成器类
    /// </summary>
    public static class IDGenerator
    {
        /// <summary>
        /// 节点类型
        /// </summary>
        private static readonly uint m_nodeType;

        /// <summary>
        /// 节点编号
        /// </summary>
        private static readonly uint m_node;
        
        /// <summary>
        /// 线程锁
        /// </summary>
        private static readonly object m_lockThis = new object();

        /// <summary>
        /// 最小时间
        /// </summary>
        private static readonly DateTime MIN_DATE_TIME = new DateTime(1970, 1, 1);

        /// <summary>
        /// 开始时间截 (2018-01-01)
        /// </summary>
        private const long TWEPOCH = 1546300800000L;

        /// <summary>
        /// 机器id所占的位数,节点ID范围在0-127
        /// </summary>
        private const int NODE_BITS = 7;

        /// <summary>
        /// 机器id最小值
        /// </summary>
        private const int MIN_NODE = 0;

        /// <summary>
        /// 机器id最大值
        /// </summary>
        private const int MAX_NODE = (-1 ^ (-1 << NODE_BITS));

        /// <summary>
        /// 序列在id中占的位数
        /// </summary>
        private const int SEQUENCE_BITS = 9;

        /// <summary>
        /// 节点类型所占位数,节点类型范围在0-63
        /// </summary>
        private const int NODE_TYPE_BITS = 6;

        /// <summary>
        /// 节点类型的最小值
        /// </summary>
        private const int MIN_NODE_TYPE = 0;

        /// <summary>
        /// 节点类型最大值
        /// </summary>
        private const int MAX_NODE_TYPE = (-1 ^ (-1 << NODE_TYPE_BITS));

        /// <summary>
        /// 机器id向左移6位
        /// </summary>
        private const int NODE_SHIFT = NODE_TYPE_BITS;

        /// <summary>
        /// 生成序列向左移13位(7+6)
        /// </summary>
        private const int SEQUENCE_SHIFT = NODE_BITS + NODE_SHIFT;

        /// <summary>
        /// 时间截向左移22位(9+7+6)
        /// </summary>
        private const int TIME_STAMP_SHIFT = SEQUENCE_BITS + SEQUENCE_SHIFT;

        /// <summary>
        /// 生成序列的掩码,这里为512 (0b111111111=0x200=512)
        /// </summary>
        private const int SEQUENCE_MASK = -1 ^ (-1 << SEQUENCE_BITS);

        /// <summary>
        /// 上一次生成ID的总时间毫秒数
        /// </summary>
        private static long m_lastTimestamp;

        /// <summary>
        /// 毫秒内序列
        /// </summary>
        private static long m_sequence;

        /// <summary>
        /// 根据节点类型和节点编号生成新ID
        /// </summary>
        /// <returns>新生成的ID</returns>
        public static long NextID()
        {
            return CreateNewID(m_nodeType, m_node);
        }

        /// <summary>
        /// 获取从1970-01-01至今的毫秒数
        /// </summary>
        private static long GetTicks()
        {
            return (long)(DateTime.Now - MIN_DATE_TIME).TotalMilliseconds;
        }

        /// <summary>
        /// 阻塞线程直到下一毫秒
        /// </summary>
        /// <returns>阻塞之后的总时间毫秒数</returns>
        private static long BlockUntilNextMillis(long timestamp)
        {
            while (timestamp <= m_lastTimestamp)
                timestamp = GetTicks();

            return timestamp;
        }

        private static long CreateNewID(uint nodeType, uint node)
        {
            lock (m_lockThis)
            {
                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                if (nodeType > MAX_NODE_TYPE || nodeType < MIN_NODE_TYPE)
                    throw new Exception("节点类型错误。");

                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                if (node > MAX_NODE || node < MIN_NODE)
                    throw new Exception("节点编号异常。");

                long timestamp = GetTicks();

                if (timestamp < m_lastTimestamp) //当前时间小于上一次ID生成的时间戳
                    throw new Exception("系统时间异常。");

                if (timestamp == m_lastTimestamp) //如果是同一时间生成的,则进行毫秒内序列
                {
                    m_sequence = (m_sequence + 1) & SEQUENCE_MASK;

                    if (m_sequence == 0) //毫秒内序列溢出
                        timestamp = BlockUntilNextMillis(timestamp); //阻塞到下一个毫秒,获得新的时间戳
                }
                else //时间戳改变,毫秒内序列重置
                {
                    m_sequence = 0L;
                }

                m_lastTimestamp = timestamp; //上次生成ID的时间截

                return ((timestamp - TWEPOCH) << TIME_STAMP_SHIFT) | // 时间差占用41位,最多69年,左移22位
                       (m_sequence << SEQUENCE_SHIFT) | // 毫秒内序列,取值范围0-511,左移13位
                       (node << NODE_SHIFT) | // 工作机器,取值范围0-127
                       nodeType; // 生成方式占用6位
            }
        }

        /// <summary>
        /// 从ID获取生成ID的时间
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>生成ID的时间</returns>
        public static DateTime GetTimeFromID(long id)
        {
            return MIN_DATE_TIME.AddMilliseconds((id >> TIME_STAMP_SHIFT) + TWEPOCH);
        }

        /// <summary>
        /// 从ID获取节点编号
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>节点编号</returns>
        public static int GetMachineNodeFromID(long id)
        {
            return (int)(id >> NODE_SHIFT) & MAX_NODE;
        }

        /// <summary>
        /// 从ID获取节点类型
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>节点类型</returns>
        public static long GetMethodFromID(long id)
        {
            return id & MAX_NODE_TYPE;
        }

        static IDGenerator()
        {
            m_nodeType = Convert.ToUInt32(ConfigManager.Configuration.GetSection("NodeType").Value);
            m_node = Convert.ToUInt32(ConfigManager.Configuration.GetSection("Node").Value);
        }
    }
}

源码在库gitee链接地址:https://gitee.com/chengdu-dotneter/Framework

  有兴趣的小伙伴可以看看

posted @ 2023-08-21 14:22  六月Talk  阅读(91)  评论(0)    收藏  举报