360提交网站入口,在线玩游戏网页,wordpress注册没有密码,动漫设计与制作学什么google/uuid 库地址 google/uuid 时间相关的部分汇聚在 uuid 包下的 time.go 文件中。
UUID 的 RFC 4122 变体中的版本1和版本2依赖于时钟信息#xff0c;所以 uuid 库将时钟信息的实现定义在本文件中#xff0c;供对应版本 UUID 的生成使用。
UUID 依赖于时钟信息的实现版… google/uuid 库地址 google/uuid 时间相关的部分汇聚在 uuid 包下的 time.go 文件中。
UUID 的 RFC 4122 变体中的版本1和版本2依赖于时钟信息所以 uuid 库将时钟信息的实现定义在本文件中供对应版本 UUID 的生成使用。
UUID 依赖于时钟信息的实现版本包含两方面的时钟信息时间和时钟序列。下面将针对时间和时钟序列进行详细介绍。
时间
UUID版本1和版本2的规范根据RFC 4122使用的是自1582年10月15日以来的100纳秒数作为时间戳。
这部分信息被存储在 int64 类型中其声明如下
type Time int64而时间戳的生成可以拆分为两部分go 原生支持的 time.Now().UnixNano 和 Unix 纪元与格里历改日的差。
go 原生支持的 time.Now().UnixNano() 可以获取到距离 Unix 纪年1970年1月1日00:00:00 UTC的纳秒数而我们需要的是距离格里历改日1582年10月15日的100纳秒数所以除了对 time.Now().UnixNano() 除以 100 外还需要补充 Unix 纪元到格里历改日的差。
其实现如下
const (lillian 2299160 // 1582年10月15日的儒略日unix 2440587 // 1970年1月1日的儒略日epoch unix - lillian // 两个纪元之间的天数g1582 epoch * 86400 // 两个纪元之间的秒数g1582ns100 g1582 * 10000000 // 两个纪元之间的100纳秒数
)其先定义Unix 纪元和格里历改日的儒略日常量然后将其做差得到两个纪元之间的天数乘以每天的总秒数 86400最后乘以 10^7 得到两个纪元之间的100纳秒数。
所以最后时间戳的实现如下
now : uint64(t.UnixNano()/100) g1582ns100时钟序列
时钟序列clock sequence在UUID特别是版本1和版本2中的使用主要是为了处理两种特定的情况以确保UUID的唯一性 时钟回拨如果系统时钟被设置回一个较早的时间那么在此期间生成的UUID可能会与之前生成的UUID发生冲突因为它们可能会有相同的时间戳部分。通过在时钟回拨时改变时钟序列可以保证即使在相同的时间戳下生成的UUID也是唯一的。 快速生成UUID在非常短的时间内小于UUID时间戳分辨率的时间内生成大量的UUID时可能会耗尽给定时间戳内的所有可能的UUID。时钟序列提供了一种机制允许在这种情况下通过改变时钟序列来继续生成唯一的UUID而不是等待下一个时间戳。
但在 google/uuid 库中只用于解决时钟回拨只有但发生时钟回拨时才会增加时钟序列。
时钟序列通常占 16 位2字节最高的两位被固定用于特定目的在此库中用于标识变体。
时钟序列在 google/uuid 库中的实现逻辑书写于 setClockSequence 函数中实现原理是先生成两个 byte(uint8) 的序列然后拼凑成一个 uint16 的序列最后抹除高2位并在最高位设置变体标识1。
func setClockSequence(seq int) {// 当传入 -1 时代表着需要随机生成一个时钟序列值。if seq -1 {// 长度为 2 的字节切片数组用于存储随机生成的字节。var b [2]byte// randomBits 向 b 中随机填充字节。randomBits(b[:]) // b[0] 成为 seq 的高 8 位b[1] 为低 8 位。seq int(b[0])8 | int(b[1])}// 将当前的时钟序列值保存到oldSeq变量中以便后续比较。oldSeq : clockSeq// 设置我们的变体clockSeq uint16(seq0x3fff) | 0x8000// 时钟回拨导致时钟序列调整调整时钟序列后更新 lasttimeif oldSeq ! clockSeq {lasttime 0}
}这段代码的难点有两处
首先是
seq int(b[0])8 | int(b[1])实现我们知道我么使用 randomBits(b[:]) 的目的就是往切片数组 b 中填充数据并且 byte 在 Go 中也等同于 uint8。那么在这段代码中b 数组相当于存储了两个 uint8 的数。int(b[0]) 意味着将 uint8 转为 intint 虽然不同系统位数不同但是相比于 uint8 都有一个特点就是拥有更多的位数。int(b[1]) 也同理。现在我们假设 b[0] 二进制数表示为10101010b[1] 二进制数表示为01010101。则int(b[0]) 二进制数表示为 00000000 00000000 00000000 10101010int(b[1]) 二进制数表示为 00000000 00000000 00000000 01010101。int(b[0])8 便是 00000000 00000000 10101010 00000000。于是 int(b[0])8 | int(b[1]) 二进制表示为 00000000 00000000 10101010 01010101。
总而言之seq int(b[0])8 | int(b[1])就是将 b 数组的两个字节转换为一个整数b[0]作为高8位b[1]作为低 8 位。这个整数将作为创建最终时钟序列值的蓝图此时 seq 虽然是 int 类型但是其只有低 16 位。
再就是
clockSeq uint16(seq0x3fff) | 0x8000我们知道 seq 类型虽然为 int但其只有低 16 位而 0x3fff 其实是 0011 11111111所以 seq0x3fff 其实就是保留低 14 位的值并抹去高 2 位的值。uint16(seq0x3fff) 便是将结果转位 uint16。0x8000 即 1000 0000 0000 0000所以 | 0x8000 其实是将最高位设置为 1表示变体1。
完整时钟信息生成
获取当前时间如果未初始化时钟序列则随机生成时钟序列得到时间戳当发生时钟回拨时增加时钟序列避免重复 UUID 的出现。记录此次生成 UUID 的时间
func getTime() (Time, uint16, error) {t : timeNow()// 如果我们还没有一个时钟序列就设置一个。if clockSeq 0 {setClockSequence(-1)}now : uint64(t.UnixNano()/100) g1582ns100// 如果当前时间与上次生成 UUID 时间相比有倒退则我们增加时钟序列if now lasttime {clockSeq ((clockSeq 1) 0x3fff) | 0x8000}// 记录此次生成 UUID 的时间lasttime nowreturn Time(now), clockSeq, nil
}