zoukankan      html  css  js  c++  java
  • 分布式ID生成器 zz

    简介

    这个是根据twitter的snowflake来写的.这里有中文的介绍.

    如上图所示,一个64位ID,除了最左边的符号位不用(固定为0,以保证生成的ID都是正数),还剩余63位可用.

    下面的代码与图中的位数分配略有不同,除了中间部分10bit工作机器id不变,时间戳和序列号的位数是可以根据自己的需求变化的,就是说,你可以把中间的工作机器ID往左挪一挪,或往右挪一挪.

    代码

        /// <summary>
        /// 64位ID生成器,最高位为符号位,始终为0,可用位数63.
        /// 实例编号占10位,范围为0-1023
        /// 时间戳和索引共占53位
        /// </summary>
        public sealed class IdCreator
        {
            private static readonly Random r = new Random();
            private static readonly IdCreator _default = new IdCreator();
    
            private readonly long instanceID;//实例编号
            private readonly int indexBitLength;//索引可用位数
            private readonly long tsMax = 0;//时间戳最大值
            private readonly long indexMax = 0;
            private readonly object m_lock = new object();
            
            private long timestamp = 0;//当前时间戳
            private long index = 0;//索引/计数器
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="instanceID">实例编号(0-1023)</param>
            /// <param name="indexBitLength">索引可用位数(1-32).每秒可生成ID数等于2的indexBitLength次方.大并发情况下,当前秒内ID数达到最大值时,将使用下一秒的时间戳,不影响获取ID.</param>
            /// <param name="initTimestamp">初始化时间戳,精确到秒.当之前同一实例生成ID的timestamp值大于当前时间的时间戳时,
            /// 有可能会产生重复ID(如持续一段时间的大并发请求).设置initTimestamp比最后的时间戳大一些,可避免这种问题</param>
            public IdCreator(int instanceID, int indexBitLength, long? initTimestamp = null)
            {
                if (instanceID < 0)
                {
                    //这里给每个实例随机生成个实例编号
                    this.instanceID = r.Next(0, 1024);
                }
                else
                {
                    this.instanceID = instanceID % 1024;
                }
    
                if (indexBitLength < 1)
                {
                    this.indexBitLength = 1;
                }
                else if (indexBitLength > 32)
                {
                    this.indexBitLength = 32;
                }
                else
                {
                    this.indexBitLength = indexBitLength;
                }
                tsMax = Convert.ToInt64(new string('1', 53 - indexBitLength), 2);
                indexMax = Convert.ToInt64(new string('1', indexBitLength), 2);
    
                if (initTimestamp != null)
                {
                    this.timestamp = initTimestamp.Value;
                }
            }
    
            /// <summary>
            /// 默认每实例每秒生成65536个ID,从1970年1月1日起,累计可使用4358年
            /// </summary>
            /// <param name="instanceID">实例编号(0-1023)</param>
            public IdCreator(int instanceID) : this(instanceID, 16)
            {
    
            }
    
            /// <summary>
            /// 默认每秒生成65536个ID,从1970年1月1日起,累计可使用4358年
            /// </summary>
            public IdCreator() : this(-1)
            {
    
            }
    
            /// <summary>
            /// 生成64位ID
            /// </summary>
            /// <returns></returns>
            public long Create()
            {
                long id = 0;
    
                lock (m_lock)
                {
                    //增加时间戳部分
                    long ts = Harry.Common.Utils.GetTimeStamp() / 1000;
    
                    ts = ts % tsMax;  //如果超过时间戳允许的最大值,从0开始
                    id = ts << (10 + indexBitLength);//腾出后面部分,给实例编号和索引编号使用
    
                    //增加实例部分
                    id = id | (instanceID << indexBitLength);
    
                    //获取计数
                    if (timestamp < ts)
                    {
                        timestamp = ts;
                        index = 0;
                    }
                    else
                    {
                        if (index > indexMax)
                        {
                            timestamp++;
                            index = 0;
                        }
                    }
    
                    id = id | index;
    
                    index++;
                }
    
                return id;
            }
    
            /// <summary>
            /// 获取当前实例的时间戳
            /// </summary>
            public long CurrentTimestamp
            {
                get
                {
                    return this.timestamp;
                }
            }
    
            /// <summary>
            /// 默认每实例每秒生成65536个ID,从1970年1月1日起,累计可使用4358年
            /// </summary>
            public static IdCreator Default
            {
                get
                {
                    return _default;
                }
            }
        }

    代码说明

    使用时,需要new一个IdCreator的实例,然后调用Create()方法,生成一个ID号.需要把IdCreator的例实赋给一个静态变量,以保证ID号的唯一性.如果是分布式部署,需要给IdCreator的构造函数传递instanceID参数,每一个部署都要有一个不同的值,范围为0-1023.

    构造函数中的indexBitLength参数,代表图中最右边的'序列号'的长度,不再固定为12bit,范围为1-32.剩下的可用位,就留给了时间戳.

    注意:IdCreator类的时间戳是按秒计的. 如果想改成毫秒,只需要将代码long ts = Harry.Common.Utils.GetTimeStamp() / 1000;改成long ts = Harry.Common.Utils.GetTimeStamp();即可.

    示例代码

        IdCreator c=new IdCreator(0,16);
        var id=c.Create();
  • 相关阅读:
    ubuntu 解决“无法获得锁 /var/lib/dpkg/lock -open (11:资源暂时不可用)”的方法
    解决VMware安装ubuntu16.04后无法全屏的问题
    力推:无限制下载神器aria2
    使用Pangolon在同一副图中,画出两个轨迹,比较误差
    Pangolin中opengl的混合(gl_blend)
    Ubuntu 16.04: How to install OpenCV
    ubuntu16.04下安装Eigen
    ubuntu16.04下安装Sophus
    svn删除账户信息
    javaweb乱码(tomcat服务器)
  • 原文地址:https://www.cnblogs.com/zeroone/p/6224211.html
Copyright © 2011-2022 走看看