如何生成分散式唯一時間戳識別符號 - vanillajava

banq 發表於 2022-01-15
Java

本文介紹了一個直接支援分散式識別符號生成的實現。

1. 分散式系統中的併發識別符號生成

每個主機都有一個預定義的唯一主機識別符號或hostId 。

TimeProvider[url=https://github.com/OpenHFT/Chronicle-Core/blob/ea/src/main/java/net/openhft/chronicle/core/time/TimeProvider.java] [/url]提供了同時最多100 個主機生成不同的主機識別符號。 

 

2. 帶有主機識別符號的納秒時間戳

DistributedUniqueTimeProvider將主機識別符號儲存在時間戳的低兩位數字中,使其更易於閱讀。

這使您可以在多達 100 臺機器上生成有保證的唯一識別符號。

同一臺機器上的多個 JVM 共享一個 hostId。

時間戳在 hostId 為 28 的機器上看起來像這樣:

2021-12-28T14:07:02.9541001 28

前面是時間:日期/時間/微秒

最後兩位數字是主機識別符號

這樣更容易在時間戳中檢視來源。

這提供了十分之一微秒(數百納秒)的解析度。 無論如何,這通常是許多系統中可用掛鐘的限制。

預設情況下,可以在命令列上使用-DhostId= xx將hostId設定為系統屬性,或者通過呼叫 

DistributedUniqueTimeProvider.INSTANCE.hostId(hostId) ;

 

使用主機識別符號加速分配

通過預配置的主機識別符號並跟蹤共享記憶體中的最新識別符號,能夠實現跨機器快速併發生成識別符號,達到每秒十億的理論極限。

方法很簡單:取當前時間,去掉低兩位數字並新增hostId,只要這高於最後一個識別符號,就可以了。

如果機器發生故障,最後一個識別符號的資訊丟失,假設重新啟動服務的時間足以確保沒有重疊。

如果服務失敗了,但機器沒有,資訊就會被保留。

 

下面程式碼使用了開源庫Chronicle Bytes支援的共享記憶體中的 MappedFile :

@Override
public long currentTimeNanos() throws IllegalStateException {
 long time provider.currentTimeNanos();
 long timeo bytes.readVolatileLong(LAST_TIME);
 long timeN = time - time % HOST_IDS + hostid;
  if (timeN > timeo && bytes.compareAndSwapLong(LAST_TIME, timeo, timeN))
     return timeN;
 return currentTimeNanosLoop();

使用 JMH測試:

On an i9-10980HK

Benchmark                                                Mode  Cnt   Score   Error  Units

DistributedUniqueTimeProviderBenchmark.currentTimeNanos  avgt   25  37.395 ± 0.391  ns/op

DistributedUniqueTimeProviderBenchmark.randomUUID        avgt   25 207.709 ± 1.586  ns/op

On a Ryzen 9 5950X
Benchmark                                                Mode  Cnt    Score   Error  Units
DistributedUniqueTimeProviderBenchmark.currentTimeNanos  avgt   25   43.557 ± 0.801  ns/op
DistributedUniqueTimeProviderBenchmark.randomUUID        avgt   25  265.285 ± 2.690  ns/op

這種方法比UUID快6倍