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,一经查实,立即删除!

相关文章

5G NR与4G LTE的技术差异

5G NR与4G LTE的技术差异 5G与4G相比&#xff0c;5G(NR)技术有以下优势点&#xff1a; 一、总体技术方面 系统消息 4G(LTE): 支持在任何条件(或情况)下始终开启所有系统消息的广播&#xff0c;导致大量资源浪费&#xff0c;且终端(UE)需要持续评估。 系统信息广播是终端(UE)…

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;有些概念可能会一带而过亦或者提供…

【matlab】回归预测/异常检测——智能优化算法极限学习机

目录 引言 基本原理 主要特点 应用领域 发展趋势 智能优化算法——蜣螂优化算法&#xff08;DBO&#xff09; 算法原理 算法特点 应用前景 代码实现 ELM训练函数——elmtrain 函数 ELM预测函数——elmpredict 函数 适应度函数 主函数 引言 极限学习机&#xff08;…

【C++设计模式】(二)设计模式简介

&#xff08;二&#xff09;设计模式简介 设计模式的概念来源于建筑学&#xff0c;最早由建筑学家克里斯托弗亚历山大&#xff08;Christopher Alexander&#xff09;在其著作《建筑的模式语言》&#xff08;A Pattern Language&#xff09;中提出。亚历山大提出了一系列在建筑…

【数据挖掘】银行信用卡风险大数据分析与挖掘

银行信用卡风险大数据分析与挖掘 1、实验目的 中国某个商业银行高层发现自家信用卡存在严重的欺诈和拖欠现象,已经影响到自身经营和发展。银行高层希望大数据分析部门采用数据挖掘技术,对影响用户信用等级的主要因素进行分析,结合信用卡用户的人口特征属性对欺诈行为和拖欠…

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

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

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

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

智能生产管理系统设计

智能生产管理系统的设计旨在提升制造业的效率、灵活性和响应速度&#xff0c;通过集成先进的信息技术&#xff08;如物联网IoT、大数据分析、人工智能AI、云计算等&#xff09;实现生产过程的智能化。以下是一些关键设计要素和步骤&#xff0c;用于构建一个高效的智能生产管理系…

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 …

【ARMv8/v9 GIC 系列 5.3 -- 系统寄存器对中断的处理】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 ARMv8/v9系统寄存器对中断的控制Group 0中断的寄存器Group 1中断的寄存器安全状态与中断分组中断处理过程中断确认处理代码中断完成处理代码ARMv8/v9系统寄存器对中断的控制 在ARM GIC 体系结构中,中断分组通过一系列系统寄存器进…

推荐 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…

Ubuntu下Qt-5.12.9创建快捷方式到桌面

由于下载完的Qt5没有桌面快捷方式&#xff0c;每次使用需要进入原文件的文件中&#xff0c;操作太过繁琐&#xff0c;以下操作将为Qtcreator在桌面创建一个快捷访问文件。 第一步&#xff1a;进入自己主目录下的桌面文件夹 cd ~/Desktop第二步&#xff1a;创建一个Qt的deskto…

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

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