前端唯一標識那些事兒

在做聊天模塊的時候,最初的消息唯一標識是msgId,在業務量小的情況下是可以滿足需求的,毫秒級的唯一衝突是很難出現的。但是當用戶量上升之後,時間戳的這種方案顯然不行。因此需要引入一種新的前端生成唯一標識的方案。

除了時間戳之外,我在公司的其他前端項目中,發現一些其他的前端唯一性標識實現,因此在這裏做一個記錄。

  • 時間戳 唯一性差(目前應用於聊天消息唯一標識)
  • random string 唯一性較強(目前應用於OSS存儲文件唯一識別名)
  • uuid 唯一性極強(待引入的唯一性更強的方案)

    • RFC4122是什麼
    • node-uuid

      • node-uuid是什麼
      • uuid的v1,v3,v4,v5分別是什麼意思?
      • 已有項目uuid應用分析
      • node-uuid項目實踐
  • 總結與思考

時間戳

應用於聊天模塊的msgId,就是採用了時間戳的形式。

this.message.msgId = `${+new Date()}`; // "1568689340401"

雖然說唯一性較差,但是截至目前還沒有出現因爲唯一性差導致重大問題。

random string

function randomString(length) {
  const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_=-';
  let result = '';
  for (let i = length; i > 0; --i) {
    result += chars[Math.floor(Math.random() * chars.length)];
  }
  return result;
}

假如length輸入的是64,那麼這個隨機數算法會在0~9,a~z,A~Z,-=_中生成一個64的64次方的分之一的隨機字符串,64的64次方式3.940200619639448e+115,億級也就1.0e+10,這個數字已經龐大到令人髮指,唯一性其實已經很強了。

唯一性會隨着length長度的下降而下降,在文件名過長的情況下調整文件名長度時需要特別注意。

uuid

node-uuid是一個基於RFC4122加密算法的nodejs實現,在現代化的前端項目中,是可以直接引用的。

UUID的全寫是Universally Unique IDentifier,可以理解爲全球唯一識別碼。(引入uuid以後,根本不用擔心自己手上的項目前端憑證唯一性不足的問題,因爲UUID是全球都唯一的。)

RFC4122是什麼

來看一段RFC4122的官方摘要就基本明白了。

Abstract
This specification defines a Uniform Resource Name namespace for
UUIDs (Universally Unique IDentifier), also known as GUIDs (Globally
Unique IDentifier). A UUID is 128 bits long, and can guarantee
uniqueness across space and time. UUIDs were originally used in the
Apollo Network Computing System and later in the Open Software
Foundation's (OSF) Distributed Computing Environment (DCE), and then
in Microsoft Windows platforms.
This specification is derived from the DCE specification with the
kind permission of the OSF (now known as The Open Group).
Information from earlier versions of the DCE specification have been
incorporated into this document.

可以提煉出以下知識點:

  • 這個規範定義了UUIDs(Universally Unique IDentifier)的統一資源名命名空間,也可以叫做GUIDs(Global Unique IDentifier)。
  • 一個UUID的長度是128位,在空間和時間兩個維度都是可以保證唯一性的。
  • UUID的首次應用,是在阿波羅網絡計算系統上,之後又應用在了開源軟件基金會(OSF)和分佈式計算環境(DCE),後來也應用在了微軟的Windows平臺上。
  • 這個規範是在OSF的許可下從DCE規範衍生出來的。(OSF指的是Open Group)
  • DCE規範早期版本中的內容合併在了這個文檔中。

從上面關於RFC4122的描述可以看出,RFC4122其實是一個UUID規範,最初誕生於阿波羅計算機,一直沿用至今。基於這個規範,有多種語言的版本。

從github上,我找到了幾種語言的基於RFC4122實現的UUID的repo。

語言 repo名 地址
php uuid https://github.com/ramsey/uuid
nodejs node-uuid https://github.com/kelektiv/n...
go go.uuid https://github.com/satori/go....
rust uuid https://github.com/uuid-rs/uuid
python shortuuid https://github.com/skorokitha...
objective-c FCUUID https://github.com/fabiocacca...

node-uuid

node-uuid是什麼
  • 符合commonjs規範和RFC4122的nodejs實現的UUID
  • 支持版本1,3,4,5多個版本的UUID
  • 跨平臺,瀏覽器端和node端皆可使用
  • 可加密-必要時可以使用隨機數API進行加密增強安全性
  • 零依賴,純粹的自給自足的package
uuid的v1,v3,v4,v5分別是什麼意思?
v1 timestamp(時間戳)

uuid.v1(options, buffer, offset)
options包括node,clockseq,msecs和nsecs;buffer是UUID將要寫入的地方;buffer起始位置。
一般來說直接vvid.v1()即可。

v3 namespace(命名空間)

uuid.v3(name, namespace, buffer, offset)
name是uuid的名字;namespace是字符串或16位數組UUID的命名空間;buffer起始位置。
一般來說直接指定name和namespace即可。

const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341';
uuid.v3('Hello, World!', MY_NAMESPACE); // ⇨ 'e8b5a51d-11c8-3310-a6ab-367563f20686'
v4 random number(隨機數)

uuid.v4(options, buffer, offset)
options包括random和rng;buffer是UUID將要寫入的地方;buffer起始位置。
一般來說直接vvid.v4()即可。

v5 namespace(命名空間)

uuid.v5(name, namespace, buffer, offset)
name是uuid的名字;namespace是字符串或16位數組UUID的命名空間;buffer起始位置。
一般來說直接指定name和namespace即可。

const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341';
uuid.v5('Hello, World!', MY_NAMESPACE); // ⇨ '630eb68f-e0fa-5ecc-887a-7c7a62614681'

從上面可以看出,UUID有時間戳,隨機數和命名空間三種版本。
v1是時間戳;v4是隨機數;v3和v5是命名空間。
根據具體業務場景選擇恰當的UUID版本。

已有項目uuid應用分析
import UUID from "uuid";
export const uuid = UUID.v4().split("-").join("")
/**
UUID.v4(); // "51a3b08b-41ce-49ca-bda3-717b22bd9b3e"
UUID.v4().split("-"); // ["51a3b08b", "41ce", "49ca", "bda3", "717b22bd9b3e"]
UUID.v4().split("-").join(""); // "51a3b08b41ce49cabda3717b22bd9b3e"
**/

生成長度爲32的uuid,將-移除。其實移除不移除都是ok的。

node-uuid項目實踐

使用node-uuid替換現有的msgId,增強消息唯一標識的唯一性。

我最初的想法是通過node-uuid的v4版本生成一個隨機數uuid,對消息做唯一標識即可。但是由於考慮到服務端的業務實現,這個方案不可行。

原因是因爲服務端需要使用時間戳類型的msgId加時間戳類型的版本號,最後消息需要根據時間戳進行排序。因此不能暴力替換,需要找一個其他的不會造成break change的方案。

node-uuid或者說UUID的v1版本就是時間戳的形式,但是能否引入到項目中還有待商榷。

import UUID from "uuid";
export const uuid = UUID.v1();// "a20c6eb0-d922-11e9-9be9-5ff126df765f"

總結與思考

  • 時間戳唯一性雖然差但是可能剛好滿足特定的業務場景,不見得是差的技術方案
  • 自定義的random string唯一識別實現,其實也能滿足唯一性的要求
  • uuid有多個版本,包括時間戳(v1),隨機數(v4),命名空間(v3,v5)
  • 永遠沒有最佳的技術方案,只要最適合業務場景的技術方案
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章