前端惟一標識那些事兒

在作聊天模塊的時候,最初的消息惟一標識是msgId,在業務量小的狀況下是能夠知足需求的,毫秒級的惟一衝突是很難出現的。可是當用戶量上升以後,時間戳的這種方案顯然不行。所以須要引入一種新的前端生成惟一標識的方案。php

除了時間戳以外,我在公司的其餘前端項目中,發現一些其餘的前端惟一性標識實現,所以在這裏作一個記錄。前端

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

    • RFC4122是什麼
    • node-uuidpython

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

時間戳

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

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

雖說惟一性較差,可是截至目前尚未出現由於惟一性差致使重大問題。github

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,這個數字已經龐大到使人髮指,惟一性其實已經很強了。objective-c

惟一性會隨着length長度的降低而降低,在文件名過長的狀況下調整文件名長度時須要特別注意。算法

uuid

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

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)
  • 永遠沒有最佳的技術方案,只要最適合業務場景的技術方案

期待和你們交流,共同進步,歡迎你們加入我建立的與前端開發密切相關的技術討論小組:

努力成爲優秀前端工程師!

相關文章
相關標籤/搜索