uidgen

package
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 10, 2026 License: MIT Imports: 13 Imported by: 4

Documentation

Overview

Package uidgen 提供分布式唯一 ID 生成器.

推荐使用 Snowflake(全局单例 + 极简 API):

uidgen.InitSnowflake(7)                  // 一次性初始化, machineID ∈ [0, 1023]
id := uidgen.SnowflakeID()               // int64, 不返错, auto-init 兜底
uidgen.ExtractSnowflakeTime(id)          // epoch = 2025-01-01
uidgen.ExtractSnowflakeMachineID(id)
uidgen.ExtractSnowflakeSequence(id)

Extract 函数(epoch = 2024-01-01)用于从旧式 ID 中提取元数据:

uidgen.ExtractTime(id)
uidgen.ExtractWorkerID(id)
uidgen.ExtractSequence(id)

UUID 系列(与 Snowflake 无关)见 uuid.go.

ULID 系列(26 字符,字典序 == 时间序)见 ulid.go——面向「文本主键 + 按 ID 做时间序 range scan」的场景:

id := uidgen.ULID()                       // 26 字符,同毫秒内严格递增
uidgen.ULIDFromTime(t)                   // 指定时间戳(按业务时间回填)
ts, entropy, err := uidgen.ParseULID(id)  // 解回时间戳与 80 bit 熵

crypto/rand 随机串生成(标识符/密钥面):与 Snowflake(可预测、仅作 ID)分居—— 密钥、一次性凭据、高熵标识符必须走本文件。

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidULID = errors.New("uidgen: invalid ULID")

ErrInvalidULID 表示字符串不是合法 ULID:长度不对、含非法字符, 或超出 128 bit 范围(首字符取值大于 '7')。

调用方用 errors.Is(err, uidgen.ErrInvalidULID) 判定;具体原因在错误消息里。

Functions

func ExtractSequence

func ExtractSequence(id int64) int64

ExtractSequence 从 ID 中提取序列号。

func ExtractSnowflakeMachineID

func ExtractSnowflakeMachineID(id int64) int64

ExtractSnowflakeMachineID 从 Snowflake ID 中提取 machineID.

func ExtractSnowflakeSequence

func ExtractSnowflakeSequence(id int64) int64

ExtractSnowflakeSequence 从 Snowflake ID 中提取 sequence.

func ExtractSnowflakeTime

func ExtractSnowflakeTime(id int64) time.Time

ExtractSnowflakeTime 从 Snowflake ID 中提取生成时间. 注意:与 ExtractTime 不可混用——epoch 不同(2025 vs 2024).

func ExtractTime

func ExtractTime(id int64) time.Time

ExtractTime 从 ID 中提取生成时间。

func ExtractWorkerID

func ExtractWorkerID(id int64) int64

ExtractWorkerID 从 ID 中提取 workerID。

func InitSnowflake

func InitSnowflake(machineID int64) error

InitSnowflake 初始化全局 Snowflake singleton(缺省容忍上限 5s,无回拨回调). machineID 必须在 [0, 1023] 范围. 多次调用:第一次生效,后续返错(防止误用).

并发安全:CAS(nil -> s) 失败表示已初始化,Load() 拿到的是上次成功的实例.

func InitSnowflakeFromEnv

func InitSnowflakeFromEnv() error

InitSnowflakeFromEnv 从 MACHINE_ID env 读 machineID,解析失败兜底 0. 单进程 dev / 测试默认 0 够用;生产必须显式注入(e.g. docker-compose 配 0/1/2).

func InitSnowflakeWith added in v0.4.2

func InitSnowflakeWith(machineID int64, opts ...SnowflakeOption) error

InitSnowflakeWith 带选项初始化全局 Snowflake singleton(#72): WithClockBackTolerance 调时钟回拨容忍上限、WithOnClockBack 注册大幅回拨 通报回调。machineID 范围与多次调用规则同 InitSnowflake.

func ParseULID added in v0.3.0

func ParseULID(s string) (time.Time, []byte, error)

ParseULID 解析 ULID,返回它的毫秒时间戳与 80 bit 熵(10 字节)。

大小写不敏感(ULID 规范如此)。长度不对、含非法字符、或超出 128 bit (首字符取值大于 '7')时返回包装了 ErrInvalidULID 的错误。

func RandomHex

func RandomHex(nBytes int) string

RandomHex n 字节 crypto/rand 随机数的十六进制串(2n 字符)。 nBytes <= 0 直接 panic(#50):对标识符场景「非法长度=调用方 bug」,对密钥/ 凭据场景静默空串是**最危险的缺省**——「名义生成了、实际是空钥匙」(空串做 HMAC key / API token 落库后患无穷),与 RandomString 空 alphabet 的防呆哲学 对齐:非法入参启动即暴露好过静默降级。长度来自配置面时请在构造期校验。 crypto/rand 读取失败(运行期环境故障)同样 panic——请求路径请用 RandomHexE(#87)。

适用:对象 ID、webhook secret、审批 HMAC 密钥等高熵标识符与密钥面。

func RandomHexE added in v0.5.0

func RandomHexE(nBytes int) (string, error)

RandomHexE 是 RandomHex 的 error 返回变体(#87):入参校验与 RandomHex 完全 一致(nBytes <= 0 仍 panic——非法入参是调用方 bug,防呆哲学不变);差异仅在 crypto/rand 读取失败这一**运行期环境故障**——panic 版会把它升级成进程崩溃, 本版返回 error,请求路径(nonce/token/上传会话 ID)可就地降级处理,不必再因 「没有 error 出口」而吞错或自己手搓 make+rand.Read+hex。

func RandomHexFrom added in v0.5.0

func RandomHexFrom(r io.Reader, nBytes int) (string, error)

RandomHexFrom 是 RandomHex 的显式随机源变体(#87):从 r 读满 nBytes 字节后 hex 编码,专供测试注入确定性/必然失败的随机源(也供调用方自带可信随机源)。 入参校验与 RandomHex 同款(nBytes <= 0 panic,checkHexArgs 单一来源);nil reader 视为非法入参 panic——显式源是调用方的承诺,nil 不是「用默认」。 读取语义 io.ReadFull:注入短读 reader(如每次只吐 3 字节)循环读满。

func RandomString

func RandomString(alphabet string, n int) string

RandomString 从 alphabet 均匀采样 n 个字符(rune 语义:多字节 alphabet 产出 合法 UTF-8,输出恰含 n 个字符;#89)——拒绝采样消模偏差,无 mod 偏置。

入参防呆(全部当场 panic):

  • n <= 0 直接 panic(#88):与 #50 对 RandomHex 的修法同口径——空串密钥是 最危险的缺省(「名义生成了、实际是空钥匙」),长度来自配置面时请在构造 期校验;
  • alphabet 为空 panic;
  • alphabet 字符数 > 256 panic(#52:拒绝采样 maxMultiple 归 0 ⇒ 死循环; #89 起判据按 rune 数计,字节口径下的多字节大字符集不再被误伤)。

crypto/rand 读取失败(运行期环境故障)同样 panic——请求路径请用 RandomStringE(#87)。 适用:带自定义字符集的短 ID/令牌。

func RandomStringE added in v0.5.0

func RandomStringE(alphabet string, n int) (string, error)

RandomStringE 是 RandomString 的 error 返回变体(#87):入参校验与 RandomString 完全一致(n <= 0 / 空 alphabet / 字符数 >256 仍 panic——非法入参是调用方 bug); 差异仅在 crypto/rand 读取失败这一运行期环境故障返回 error 而非 panic。取样经 共享核心 sampleRunes(显式随机源变体 RandomStringFrom 同款),拒绝采样实现 只此一份(无第二份 256%len 偏差修复代码)。

func RandomStringFrom added in v0.5.0

func RandomStringFrom(r io.Reader, alphabet string, n int) (string, error)

RandomStringFrom 是 RandomString 的显式随机源变体(#87):从 r 取样,与 RandomString/RandomStringE 共用同一份拒绝采样实现(sampleRunes,无第二份 256%len 偏差修复代码)。入参校验同 panic 版(n<=0 / 空 alphabet / 字符数 >256 panic,checkStringArgs 单一来源);nil reader 视为非法入参 panic。 读取语义 io.ReadFull:注入短读 reader(如每次只吐 3 字节)循环读满。

func ShortUUID

func ShortUUID() string

ShortUUID 返回一个 16 位的短 UUID。

func ShortUUIDToUpper

func ShortUUIDToUpper() string

ShortUUIDToUpper 返回大写的 16 位短 UUID。

func SnowflakeID

func SnowflakeID() int64

SnowflakeID 拿下一个 int64 ID. 未 InitSnowflake 时自动以 machineID=0 初始化.

并发说明:auto-init 路径在多 goroutine 同时首次调用时,CAS 失败的 goroutine 会回退到 Load() 读已 init 的实例,保证不会 nil deref.

时钟回拨(#72):回拨在容忍上限内(缺省 5s,InitSnowflakeWith + WithClockBackTolerance 可调)时持锁有界等待时钟追平;超限(如 NTP 大幅 回拨)转进位生成——以回拨前基准继续发号,不再阻塞,ID 时间戳领先真实 时钟直到追平(ExtractSnowflakeTime 读到的是合成时刻)。大幅回拨可用 WithOnClockBack 注册回调接告警.

func SnowflakeMachineID

func SnowflakeMachineID() int64

SnowflakeMachineID 返当前 init 用的 machine ID,未初始化返 -1,测试用.

func ULID added in v0.3.0

func ULID() string

ULID 返回 26 字符的 ULID(Universally Unique Lexicographically Sortable Identifier)。

结构 = 48 bit 毫秒时间戳(Unix 纪元)+ 80 bit 熵,Crockford Base32 编码。 适用于「文本主键 + 按 ID 做时间序 range scan」这一类需求: Snowflake 是 int64(不能直接当文本主键),UUID 是随机串(字典序与时间无关)。

同一毫秒内进程内**严格单调**:熵逐个递增,80 bit 熵用尽则进位到下一毫秒。 时钟回拨时冻结在上一次的毫秒继续递增熵,保证不会出现更小的 ID。

零第三方依赖:crypto/rand + 标准库。

func ULIDFromTime added in v0.3.0

func ULIDFromTime(t time.Time) string

ULIDFromTime 用指定时间戳生成 ULID,便于按业务时间回填 ID。

时间戳取 t 的 Unix 毫秒;熵为随机值。**不参与 ULID() 的进程内单调序列** ——回填的业务时间可能早于当前时间,混进单调序列会破坏它的单调性。 同一 t 多次调用得到的 ULID 彼此无序,需要有序时请用 ULID()。

t 超出 48 bit 毫秒时间戳范围(Unix 纪元之前,或公元 10889 年之后)时 panic: 这种时间戳编不出合法 ULID,静默截断会得到一个「时间不对但看着正常」的 ID。

func UUID

func UUID() string

UUID 返回一个不带连字符的 UUID 字符串。

func UUIDToUpper

func UUIDToUpper() string

UUIDToUpper 返回大写的 UUID 字符串。

func UUIDWithHyphen

func UUIDWithHyphen() string

UUIDWithHyphen 返回带连字符的 UUID 字符串。

Types

type Snowflake

type Snowflake struct {
	// contains filtered or unexported fields
}

Snowflake 是 singleton 模式的 Snowflake ID 生成器.

Bit 布局(bit 63 → 0):

[1 sign | 41 ts | 10 machine | 12 sequence]

= ~69 years × 1024 machines × 4096 IDs/ms per machine.

区别于旧式 ID 生成:

  • Singleton 模式,业务侧直接调 SnowflakeID() 拿下一个 int64 ID.
  • 时钟回拨不无限忙等(#72):容忍上限内(缺省 5s)持锁等时钟追平; 超限转进位生成(以回拨前基准继续发号),可注册 WithOnClockBack 告警.
  • API 极简:InitSnowflake + SnowflakeID,不暴露 mu / sequence / lastTS.

并发模型:

  • InitSnowflake 用 atomic.Pointer CAS 保证 "只 init 一次" + "成功后立刻可见".
  • SnowflakeID() auto-init 路径在 CAS 失败时回退到 Load(),避免 nil deref.
  • next() 持单 mu 串行:临界区短(同 ms 仅几次算术,跨 ms 持锁等下 ms 最坏 1ms).

Reference: https://github.com/twitter-archive/snowflake (Twitter, 2010)

type SnowflakeOption added in v0.4.2

type SnowflakeOption func(*Snowflake)

SnowflakeOption 是 Snowflake 生成器的配置选项(#72),经 InitSnowflakeWith 注入全局单例。

func WithClockBackTolerance added in v0.4.2

func WithClockBackTolerance(d time.Duration) SnowflakeOption

WithClockBackTolerance 设置时钟回拨容忍上限 d(缺省 5s,#72):回拨 ≤ d 时 维持既有行为——持锁等待时钟追平(最坏等满 d);回拨 > d 时不再等待,转 进位生成。d ≤ 0 表示任何回拨都直接进位生成。

func WithOnClockBack added in v0.4.2

func WithOnClockBack(fn func(back time.Duration)) SnowflakeOption

WithOnClockBack 注册大幅时钟回拨通报回调(#72):回拨幅度超过容忍上限时, 生成器放弃等待、转进位生成——ID 时间戳领先真实时钟直到追平,本回调以实际 回拨幅度 back 触发,每轮回拨(从进入进位生成到真实时钟追平)只触发一次。 未注册时缺省 slog.Warn。

回调在生成器持锁状态下同步调用,必须快速返回(记数/投递告警即可): 阻塞会卡住全进程的 ID 生成;再调 SnowflakeID/InitSnowflake 会死锁。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL