Go 语言 UUID 库 google/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的唯一性:

  1. 时钟回拨:如果系统时钟被设置回一个较早的时间,那么在此期间生成的UUID可能会与之前生成的UUID发生冲突,因为它们可能会有相同的时间戳部分。通过在时钟回拨时改变时钟序列,可以保证即使在相同的时间戳下,生成的UUID也是唯一的。

  2. 快速生成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(seq&0x3fff) | 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 01010101int(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(seq&0x3fff) | 0x8000

我们知道 seq 类型虽然为 int,但其只有低 16 位,而 0x3fff 其实是 0011 11111111,所以 seq&0x3fff 其实就是保留低 14 位的值并抹去高 2 位的值。uint16(seq&0x3fff) 便是将结果转位 uint160x80001000 0000 0000 0000,所以 | 0x8000 其实是将最高位设置为 1,表示变体1。

完整时钟信息生成

  1. 获取当前时间
  2. 如果未初始化时钟序列则随机生成时钟序列
  3. 得到时间戳
  4. 当发生时钟回拨时,增加时钟序列避免重复 UUID 的出现。
  5. 记录此次生成 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
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/866849.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gda动态调试-cnblog

忽的发现gda有动态调试功能 动态监听返回值 框柱指定方法&#xff0c;选择调试方法&#xff0c;gda会自动监听函数的返回值&#xff0c;例如 自定义frida脚本 gda会自动生成hook该函数的frida脚本

window.ai 开启你的内置AI之旅

❝ 成功是得你所想&#xff0c;幸福是享你所得 大家好&#xff0c;我是柒八九。一个专注于前端开发技术/Rust及AI应用知识分享的Coder ❝ 此篇文章所涉及到的技术有 AI( Gemini Nano) Chrome Ollama 因为&#xff0c;行文字数所限&#xff0c;有些概念可能会一带而过亦或者提供…

顶顶通呼叫中心中间件-外呼通道变量同步到坐席通道变量(mod_cti基于Freeswitch)

机器人伴随转人工或者排队转人工 把外呼通道同步到坐席通道变量 在拨号方案转人工动作cti_acd,或者转机器人动作cti_rotobt的前面&#xff0c;添加一个 export nolocal:变量名${变量名} 一、配置拨号方案 win-ccadmin配置方法 点击拨号方案 -> 点击进入排队 -> 根据图…

Java项目:基于SSM框架实现的中小企业人力资源管理系统【ssm+B/S架构+源码+数据库+开题报告+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的中小企业人力资源管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简…

jmeter-beanshell学习2-beanshell断言

继续写&#xff0c;之前写了获取变量&#xff0c;设置变量&#xff0c;今天先写个简单点的断言。 一般情况用响应断言&#xff0c;就挺好使&#xff0c;但是自动化还要生成报告&#xff0c;如果断言失败了&#xff0c;要保存结果&#xff0c;只能用beanshell处理&#xff0c;顺…

Ubuntu 24.04-自动安装-Nvidia驱动

教程 但在安全启动模式下可能会报错。 先在Nvidia官网找到GPU对应的驱动版&#xff0c; 1. 在软件与更新中选择合适的驱动 2. ubuntu自动安装驱动 sudo ubuntu-drivers autoinstall显示驱动 ubuntu-drivers devices3. 安装你想要的驱动 sudo apt install nvidia-driver-ve…

如何在 SwiftUI 中熟练使用 sensoryFeedback 修饰符

文章目录 前言背景介绍平台支持仅支持watchOS支持watchOS和iOS 基本用法预定义样式根据触发器值选择样式使用场景当值更改时触发使用条件闭包触发使用反馈闭包触发 可以运行 Demo总结 前言 SwiftUI 引入了新的 sensoryFeedback 视图修饰符&#xff0c;使我们能够在所有 Apple …

推荐 3个让你爽到爆炸的电脑软件,完全免费,请低调使用

Royal TS Royal TS是一款功能强大的远程系统访问工具&#xff0c;适用于服务器管理员、系统工程师、开发人员和专注于IT的信息工作者。它支持多种协议&#xff08;如RDP、VNC、SSH、HTTP/S等&#xff09;&#xff0c;使得用户能够方便地管理远程系统的连接。通过定义管理连接&a…

无人机常见故障及维修方法详解

一、无人机故障识别与处理原则 无人机故障识别是维修的第一步&#xff0c;要求操作人员具备基本的无人机系统知识和故障识别能力。在识别故障时&#xff0c;应遵循“先易后难、先外后内、先软件后硬件”的原则。一旦识别出故障&#xff0c;应立即停止飞行&#xff0c;避免进一…

Java经典面试题将一个字符串数组进行分组输出,每组中的字符串都由相同的字符组成

Java经典面试题将一个字符串数组进行分组输出&#xff0c;每组中的字符串都由相同的字符组成 题目&#xff1a; 将一个字符串数组进行分组输出&#xff0c;每组中的字符串都由相同的字符组成 举个例子&#xff1a;输入[“eat”,“tea”,“tan”,“ate”,“nat”,“bat”] 输出…

高性价比模块:LSYT201B语音模块学习使用

最近打算做个语音的项目&#xff0c;找到了深圳雷龙发展的LSY201B这款语音模块&#xff0c;写出来安利一下 程序源码&#xff1a;SuiXinSc/Speech-Module (github.com) 或者进入Q群找我获取 目录 一&#xff0c;简要介绍&#xff1a; 硬件参数&#xff1a; 1&#xff0c;处理…

add_metrology_object_generic 添加测量模型对象。找两条直线,并计算两条线的夹角和两个线的总长度,转换成毫米单位

*添加测量模型对象 *将测量对象添加到测量模型中 *算子参数&#xff1a; *    MeasureHandle&#xff1a;输入测量模型的句柄&#xff1b; *    Shape&#xff1a;输入要测量对象的类型&#xff1b;默认值&#xff1a;‘circle’&#xff0c;参考值&#xff1a;‘circl…

蓝牙模块功耗优化技术研究

蓝牙模块作为无线通信技术的重要组成部分&#xff0c;在智能家居、可穿戴设备、医疗健康等领域得到了广泛应用。然而&#xff0c;随着设备功能的不断增加和用户对续航能力的日益关注&#xff0c;蓝牙模块的功耗问题逐渐凸显。因此&#xff0c;对蓝牙模块功耗优化技术的研究具有…

154. 寻找旋转排序数组中的最小值 II(困难)

154. 寻找旋转排序数组中的最小值 II 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;154. 寻找旋转排序数组中的最小值 II 2.详细题解 该题是153. 寻找旋转排序数组中的最小值的进阶题&#xff0c;在153. 寻找旋转排序数组中的最小值…

2024年7月6日 十二生肖 今日运势

小运播报&#xff1a;2024年7月6日&#xff0c;星期六&#xff0c;农历六月初一 &#xff08;甲辰年庚午月辛未日&#xff09;&#xff0c;法定节假日。 红榜生肖&#xff1a;猪、马、兔 需要注意&#xff1a;狗、鼠、牛 喜神方位&#xff1a;西南方 财神方位&#xff1a;正…

【介绍下JSON,JSON是什么?】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 💡JSON讲解 💡1. 介绍💡2. JSON的基本规则💡3. JSON中的数…

Linux配置固定ip地址

虚拟机的Linux操作系统&#xff0c;其IP地址是通过DHCP服务获取的 DHCP&#xff1a;动态获取IP地址&#xff0c;即每次重启设备后都会获取一次&#xff0c;可能导致IP地址频繁变更。 一般系统默认的ip地址设置都是自动获取&#xff0c;故每次系统重启后ip地址都可能会不一样&a…

【C语言】typedef 关键字

在C语言中&#xff0c;typedef关键字用于给现有的数据类型起一个新的名字。它在提高代码可读性、简化复杂类型声明、增强可维护性方面非常有用。typedef通常用于定义结构体、指针、函数指针以及其他复杂类型。 基本用法 typedef int MyInt; MyInt x 10;在这个例子中&#xf…

植物学(书籍学习资料)

包含观赏植物学、植物学、植物学百科图鉴等多本植物学方面的书籍学习资料。 图2、3为观赏植物学截图&#xff1b; 图4、5为植物学百科图鉴截图&#xff1b; 图6、7为植物学学习指南截图。

RocketMQ-订阅一致及解决方案

背景 这里借用Rocketmq官方的一句话来描述订阅关系一致: 订阅关系一致指的是同一个消费者分组Group ID下&#xff0c;所有Consumer实例所订阅的Topic和Tag必须完全一致。如果订阅关系不一致&#xff0c;可能导致消息消费逻辑混乱&#xff0c;消息被重复消费或遗漏。 具体的问题…