天天看点

谈谈雪花算法的使用

谈谈雪花算法的使用

背景

618来临之际,为了应对一些突发流量,购买了两台一个月的ECS用来临时对部分项目扩容。其中一个项目有用到雪花算法来生成Id,这个还是挺OK的。

不过发现要在配置文件中手动配置机器码!!配置的时候还要先知道目前配置了那些,这样才可以避免重复。

经过了解,除了会有单机单实例的情况,还会有单机多实例的情况。

这个要人工配置,是徒增工作量的,有点让人难以接受。

针对这个,老黄就做了一点调整,让这个机器码自动生成。

雪花算法基础

关于雪花算法,大部分文章都可以看到这个图。这个图很好的诠释了雪花算法生成Id的几个重要组成部分,这里也不展开具体介绍了。

谈谈雪花算法的使用

时间戳,工作机器Id,序列号这些位数是可以根据自己的业务场景来进来调整的。

10bit工作机器Id,其实就是上面说到的机器码,雪花算法内部并没有做任何处理,而是交由业务方自己定义,所以业务方需要自己保证这个的唯一性。

大部分情况,会把它分为5bit数据中心标识和5bit机器Id。这样的话可以支持32个数据中心和32个机器Id。

换句话说就是,一个业务可以在一个数据中心部署32个实例,最多部署的32个数据中心。正常来说,大部分项目,都不会需要部署这么多实例。。。

考虑到内网的IP段基本上是固定的,同一个应用基本上也会在连续的IP上面部署。

所以这里老黄最后采用的是本地IP地址取余做为机器Id,机器的HostName取余做为默认的数据中心Id。

下面来看看具体的实现。

简单实现

自动获取机器Id和数据中心Id。

/// <summary>     /// 获取机器Id     /// </summary>     /// <returns></returns>     private int GetWorkerId()     {         var workerId = 0;         try         {             IPAddress ipaddress = IPAddress.Parse("0.0.0.0");             NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();             foreach (NetworkInterface ni in interfaces)             {                 if (ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet)                 {                     foreach (UnicastIPAddressInformation ip in                         ni.GetIPProperties().UnicastAddresses)                     {                         if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)                         {                             ipaddress = ip.Address;                             break;                         }                     }                 }             }             Console.WriteLine($"ip = {ipaddress.ToString()}");             var val = ipaddress.GetAddressBytes().Sum(x => x);             // 取余             workerId = val % (int)MaxWorkerId;         }         catch         {             // 异常的话,生成一个随机数             workerId = new Random().Next((int)MaxWorkerId - 1);         }         return workerId;     }     /// <summary>     /// 获取数据中心Id     /// </summary>     /// <returns></returns>     private int GetDatacenterId()     {         var hostName = Dns.GetHostName();         Console.WriteLine($"hostname = {hostName}");         var val = System.Text.Encoding.UTF8.GetBytes(hostName).Sum(x => x);         // 取余         return val % (int)MaxDatacenterId;     }           

生成器的构造函数

public IdGenerator(long datacenterId = -1)     {         if (datacenterId == -1)         {             // default             datacenterId = GetDatacenterId();         }         if (datacenterId > MaxDatacenterId || datacenterId < 0)         {             throw new ArgumentException("非法数据标志ID", nameof(datacenterId));         }         // 先检验再赋值         WorkerId = GetWorkerId();         DatacenterId = datacenterId;         Console.WriteLine($"w = {WorkerId}");         Console.WriteLine($"d = {DatacenterId}");     }           

这里的数据中心可以让使用方自己定义,默认-1的话,会根据HostName去生成一个。

这里给一个自定义的可选标识,主要还是考虑到了单机多实例,即一个IP上面部署多个实例。

虽然这个时候还是要考虑人工配置,不过已经从多机变成单机了,也算是一点简化。毕竟大部分情况下也不会建议在同一个机器部署多个一样的项目。

默认情况下的使用,IdGenerator对象要全局唯一,做成单例即可。

IdGenerator generator = new IdGenerator();     Parallel.For(0, 20, x =>     {         Console.WriteLine(generator.NextId());      });     Console.WriteLine("Hello World!");     System.Threading.Thread.Sleep(1000 * 60);           

下面运行多个容器来模拟。

谈谈雪花算法的使用

可以看到机器Id和数据中心Id都是没有重复的。

在运行一次。

谈谈雪花算法的使用

也是同样的。

不足与展望

目前这种做法在应用实例少,机器数量少的情况下是基本可以满足使用要求的了。老黄公司目前也就不到30台服务器,所以怎么都是够用的。

但是依靠IP和HostName,随着实例或机器的数量增多,没有办法保证它们取余算出来的一定是唯一的。

在这种情况下就需要考虑引用第三方存储(Redis或数据库)来保证这个的唯一性了。

下面是本文的示例代码:

SnowflakeDemo

谈谈雪花算法的使用

如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!

作者:Catcher Wong ( 黄文清 )

来源:http://catcher1994.cnblogs.com/

声明:

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果您发现博客中出现了错误,或者有更好的建议、想法,请及时与我联系!!如果想找我私下交流,可以私信或者加我微信。

继续阅读