赞
踩
关于 UUID 的总体介绍可以查看这篇文章,其包含阅读此篇文章的前置内容。
UUID version 1 在 RFC 4122 文件中定义,其实现基于节点 ID、时钟序列以及当前时间(距离格里历改日【1582年10月15日】 的100纳秒数,具体介绍可以看Go 语言 UUID 库 google/uuid 源码解析:时钟信息)。
目前还没有详细的文章介绍节点 ID 的实现,但是可以知道的是,节点 ID 是利用网络接口硬件地址生成的,定义在 node.go 文件的 setNodeInterface 函数中。其逻辑大致如下:
UUID version 1 在 google/uuid 中的实现则定义在 version1.go 文件中。
UUID version 1 定义的接口为 NewUUID()
,其返回值为 (UUID, error)
即返回 UUID 序列以及错误信息。其具体代码放在文章末尾,存在困惑的地方,可以看看源码。
首先我们知道 UUID 实际是长 16 字节的序列,其表现是 32 个十六进制数。google 则是将 UUID 序列使用长 16 的字节切片进行存储。其实现如下:
type UUID [16]byte
将长 16 的字节切片起别名为 UUID,使其含义更加清晰。var uuid UUID
时间戳与时钟序列通过 GetTime()
函数直接获取。(GetTime()
的详细介绍可以看 Go 语言 UUID 库 google/uuid 源码解析:时钟信息)。得到两个变量 now
和 seq
,now, seq, err := GetTime()
。
首先我们需要知道获取到的 now
类型为 int64
,即其二进制有 64 位,uuid 中的时间信息会被“切割”为三段:timeHi(16)、timeMid(16)、timeLow(32),具体“切割”如下:
xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
timeHi /timeMid /timeLow
需要提起知道的是类似 x & 0xff
的代码用于保留低位比特值,抹除高位比特值。示例如下:
x: 10101010 11011011
0xff: 00000000 11111111
-------------------------
&: 00000000 11011011
“切割”实现代码如下:
// 低32位
timeLow := uint32(now & 0xffffffff)
// 高32位中的低16位
timeMid := uint16((now >> 32) & 0xffff)
// 高16位
timeHi := uint16((now >> 48) & 0x0fff)
上述代码详解如下:
now & 0xffffffff
:取 now(int64) 的低 32 位。uint32(x)
:将结果转为 uint32。now >> 32
将 now(int64) 的高 32 位挪到低 32 位,高 32 位置 0。(x) & 0xffff
取当前(新)低 32 位中的低 16 位。uint16(x)
将结果转为 uint 16。now >> 48
将 now(int64) 的高 16 位挪到低 16 位,高 48 位置 0。(x) & 0xfff
取当前(新)低 16 位中的低 12 位。uint16(x)
将结果转位 uint 16。之所以 timeHi 只取到低 12 位,是因为需要保留 4 位作为标志位,此次是用于标识 UUID 版本。
我们需要提前知道的是:类似于 x |= 0x1000
的代码,使用于将某个特殊位置为 1 的,此次是将第 13 位(从右往左)置为1:
x: 00000011 00110011
0x1000: 00010000 00000000
--------------------------
|: 00010000 00110011
标识版本代码如下:
timeHi |= 0x1000 // 版本 1
首先我们需要知道最终的 uuid 结构组成如何:
(
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。