当前位置: 首页 > news >正文

Go 语言 UUID 库 google/uuid 源码解析:UUID version4 的实现

google/uuid 库地址

本文将解析 googl/uuid 库中 UUID 变体10版本4的实现。

版本 4 的 UUID 采取完全随机的方式实现,简单来说就是将 UUID 中的 122 位全部随机填充(剩余的 6 位作标记位)。版本 4 的 UUID 存在一定的重复风险,但就如源码注释中所说:“一年内创建几十万亿个 UUID 并出现一个重复的概率,就如同一个人一年被流星击中的风险一样(估计为 170 亿分之一)”

UUID 版本4 的实现代码写在 version4 中,提供了两种不同的实现方式,一种是直接通过随机生成器 io.Reader 生成,另一种则是通过随机池生成。

无随机池生成(随机生成器直接生成)

这种生成方法定义在 NewRandomFromReader 中,NewRandomFromReader 接收 io.Reader 作为参数,并返回 UUID 和 error。其代码很简单:

// NewRandomFromReader 根据从给定 io.Reader 读取的字节返回一个 UUID。
func NewRandomFromReader(r io.Reader) (UUID, error) {var uuid UUID_, err := io.ReadFull(r, uuid[:])if err != nil {return Nil, err}uuid[6] = (uuid[6] & 0x0f) | 0x40 // 版本 4uuid[8] = (uuid[8] & 0x3f) | 0x80 // 变体为 10return uuid, nil
}

可以拆解为两部分,一部分随机填充 UUID,另一部分标志版本和变体。

随机填充最关键的部分便是 io.Reader 和 io.ReadFull。

Reader 是定义在 io 包中的接口,是所有输入操作的基础,它是读取数据行为的抽象。在 Reader 接口中存在一个 Read 方法,会将读取到的是数据输出到字节切片 p 中:

type Reader interface {Read(p []byte) (n int, err error)
}

任何实现了 Read 方法的对象都可以视为一个 Reader。

而 NewRandomFromReader 函数由另一个内部函数调用,使用的是 rand.Reader 即定义在 rand.go 中的 Reader,其 “Reader 是一个全局的,共享的密码学安全随机数生成器实例。”(go 源码注释)

而 io.ReadFull 是一个辅助函数,用于辅助 io.Read 将切片填满。

所以 _, err := io.ReadFull(r, uuid[:]) 的含义便是使用密码学安全的随机数填满切片 uuid(16 字节的切片)。

而标识部分可以查看此篇文章Go 语言 UUID 库 google/uuid 源码解析:UUID version1 的实现的 分割时间信息 的末尾。

随机池生成

这种生成方法定义在 newRandomFromPool 中 代码如下:

func newRandomFromPool() (UUID, error) {var uuid UUIDpoolMu.Lock()if poolPos == randPoolSize {_, err := io.ReadFull(rander, pool[:])if err != nil {poolMu.Unlock()return Nil, err}poolPos = 0}copy(uuid[:], pool[poolPos:(poolPos+16)])poolPos += 16poolMu.Unlock()uuid[6] = (uuid[6] & 0x0f) | 0x40 // 版本 4uuid[8] = (uuid[8] & 0x3f) | 0x80 // 变体为 10return uuid, nil
}

可以也拆解为两部分,一部分从随机池读取 UUID,另一部分标志版本和变体。

从随机池读取 UUID 的原理就是预设一个随机池并预填充数据,当需要数据时,便从随机池中截取数据。而随机池也是一个切片 pool = [randPoolSize]byte,而 randPoolSize 则硬编码为 16 * 16,所以随机池其实是一个 16 * 16 字节的切片。与 pool 配合使用的还有 poolMupoolPos,poolMu 用于避免并访问随机池切片,而 poolPos 则是用于指定当前 UUID 的切片范围,每次生成 UUID 时即从随机池中拷贝 poolPospoolPos + 16 的数据(copy(uuid[:], pool[poolPos:(poolPos+16)])),然后 poolPos += 16 更改下次 UUID 的范围来避免获取到相同的数据。如果判断到已经到达随机池的上限了,则使用 io.ReadFull 重新填充随机池。

if poolPos == randPoolSize {_, err := io.ReadFull(rander, pool[:])if err != nil {poolMu.Unlock()return Nil, err}poolPos = 0
}

值得一提的是 poolPos 在定义的时候便是 poolPos = randPoolSize,所以在第一次生成 UUID 的时候便是随机池第一次被填充的时候。

如何决定什么时候使用随机池

对于 version4 还有个重要的参数就是布尔类型的 poolEnabledpoolEnabled 用于指定是否使用随机池,其默认值为 false,其搭配使用的是 EnableRandPoolDisableRandPool,分别用于启动缓冲池和关闭缓冲池,其实现逻辑简单说就是将 poolEnabled 设置为 truefalse

而利用到 poolEnabled 的是函数 NewRandom

func NewRandom() (UUID, error) {if !poolEnabled {return NewRandomFromReader(rander)}return newRandomFromPool()
}

poolEnabledtrue 则调用 newRandomFromPool,否则调用NewRandomFromReader

NewRandom 再往上层封装便是用户接口 New 了:

func New() UUID {return Must(NewRandom())
}

此处的 Must 用处便是检查 NewRandow 是否返回了 error。

到这里一个完整的 UUID 版本4便完成了。

以上就是 UUID 版本4实现的所有内容,希望你能有所收获。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Apache功能配置:访问控制、日志分割; 部署AWStats日志分析工具
  • MySQL 面试真题(带答案)
  • pxe高效网络批量装机
  • 0基础学会在亚马逊云科技AWS上利用SageMaker、PEFT和LoRA高效微调AI大语言模型(含具体教程和代码)
  • 服务客户,保证质量:腾讯云产品的质量实践
  • 医疗健康信息的安全挑战与隐私保护最佳实践
  • 【周末闲谈】Stable Diffusion会魔法的绘画师
  • Facebook软体机器人与机器人框架:创新社交互动的未来
  • HarmonyOS 界面开发基础篇
  • 算法日常练习
  • JAVA简单封装UserUtil
  • LangChain与正则表达式:探索文本匹配的强大工具
  • 【数据结构】--- 堆的应用
  • 什么是SQL锁
  • ES6 Iterator 与 for...of 循环(五)
  • css布局,左右固定中间自适应实现
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • Java-详解HashMap
  • leetcode讲解--894. All Possible Full Binary Trees
  • Webpack 4 学习01(基础配置)
  • yii2中session跨域名的问题
  • 从0实现一个tiny react(三)生命周期
  • 回顾2016
  • 技术胖1-4季视频复习— (看视频笔记)
  • 前端面试题总结
  • 数据可视化之 Sankey 桑基图的实现
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 用 Swift 编写面向协议的视图
  • Python 之网络式编程
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (一)Dubbo快速入门、介绍、使用
  • (转)IOS中获取各种文件的目录路径的方法
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • .bashrc在哪里,alias妙用
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .net 发送邮件
  • .net 反编译_.net反编译的相关问题
  • .net 怎么循环得到数组里的值_关于js数组
  • .NET是什么
  • @ModelAttribute注解使用
  • @vue/cli 3.x+引入jQuery
  • [ NOI 2001 ] 食物链
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...
  • [15] 使用Opencv_CUDA 模块实现基本计算机视觉程序
  • [Android]常见的数据传递方式
  • [Asp.net MVC]Asp.net MVC5系列——Razor语法
  • [Django学习]查询过滤器(lookup types)
  • [flask] flask的基本介绍、flask快速搭建项目并运行
  • [fsevents@^2.1.2] optional install error: Package require os(darwin) not compatible with your platfo
  • [Go 微服务] Kratos 验证码业务
  • [hibernate]基本值类型映射之日期类型
  • [linux time命令学习篇] time 统计命令执行的时间
  • [Linux] PXE批量装机