在作聊天模块的时候,最初的消息惟一标识是msgId,在业务量小的状况下是能够知足需求的,毫秒级的惟一冲突是很难出现的。可是当用户量上升以后,时间戳的这种方案显然不行。所以须要引入一种新的前端生成惟一标识的方案。php
除了时间戳以外,我在公司的其余前端项目中,发现一些其余的前端惟一性标识实现,所以在这里作一个记录。前端
应用于聊天模块的msgId,就是采用了时间戳的形式。node
this.message.msgId = `${+new Date()}`; // "1568689340401"
复制代码
虽说惟一性较差,可是截至目前尚未出现由于惟一性差致使重大问题。python
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,这个数字已经庞大到使人发指,惟一性其实已经很强了。git
惟一性会随着length长度的降低而降低,在文件名过长的状况下调整文件名长度时须要特别注意。github
node-uuid是一个基于RFC4122加密算法的nodejs实现,在现代化的前端项目中,是能够直接引用的。objective-c
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 | github.com/ramsey/uuid |
nodejs | node-uuid | github.com/kelektiv/no… |
go | go.uuid | github.com/satori/go.u… |
rust | uuid | github.com/uuid-rs/uui… |
python | shortuuid | github.com/skorokithak… |
objective-c | FCUUID | github.com/fabiocaccam… |
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"
复制代码