在作聊天模块的时候,最初的消息惟一标识是msgId,在业务量小的状况下是能够知足需求的,毫秒级的惟一冲突是很难出现的。可是当用户量上升以后,时间戳的这种方案显然不行。所以须要引入一种新的前端生成惟一标识的方案。php
除了时间戳以外,我在公司的其余前端项目中,发现一些其余的前端惟一性标识实现,所以在这里作一个记录。前端
uuid 惟一性极强(待引入的惟一性更强的方案)node
node-uuidpython
应用于聊天模块的msgId,就是采用了时间戳的形式。git
this.message.msgId = `${+new Date()}`; // "1568689340401"
虽说惟一性较差,可是截至目前尚未出现由于惟一性差致使重大问题。github
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长度的降低而降低,在文件名过长的状况下调整文件名长度时须要特别注意。算法
node-uuid是一个基于RFC4122加密算法的nodejs实现,在现代化的前端项目中,是能够直接引用的。segmentfault
UUID的全写是Universally Unique IDentifier,能够理解为全球惟一识别码。(引入uuid之后,根本不用担忧本身手上的项目前端凭证惟一性不足的问题,由于UUID是全球都惟一的。)数组
来看一段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.
能够提炼出如下知识点:
从上面关于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... |
uuid.v1(options, buffer, offset)
options包括node,clockseq,msecs和nsecs;buffer是UUID将要写入的地方;buffer起始位置。
通常来讲直接vvid.v1()
便可。
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'
uuid.v4(options, buffer, offset)
options包括random和rng;buffer是UUID将要写入的地方;buffer起始位置。
通常来讲直接vvid.v4()
便可。
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版本。
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替换现有的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"
期待和你们交流,共同进步,欢迎你们加入我建立的与前端开发密切相关的技术讨论小组:
- SegmentFault技术圈:ES新规范语法糖
- SegmentFault专栏:趁你还年轻,作个优秀的前端工程师
- 知乎专栏:趁你还年轻,作个优秀的前端工程师
- Github博客: 趁你还年轻233的我的博客
- 前端开发QQ群:660634678
- 微信公众号: 人兽鬼 / excellent_developers
努力成为优秀前端工程师!