天天看點

談談雪花算法的使用

談談雪花算法的使用

背景

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/

聲明:

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。如果您發現部落格中出現了錯誤,或者有更好的建議、想法,請及時與我聯系!!如果想找我私下交流,可以私信或者加我微信。

繼續閱讀