// ids.go:请求 ID 生成(ulid 形态语义:时间有序 + 随机;无外部依赖自实现)。 package gateway import ( "crypto/rand" "sync" "time" ) // idChars Crockford base32(ulid 兼容字符集,26 字符编码空间)。 const idChars = "0123456789abcdefghjkmnpqrstvwxyz" // idMu 单调状态(同毫秒内保证有序)。 var ( idMu sync.Mutex idLast int64 idSeq uint8 ) // newRequestID 生成 26 字符 ulid 风格请求 ID(48bit 毫秒时间 + 80bit 随机)。 func newRequestID() string { ms := time.Now().UnixMilli() idMu.Lock() if ms == idLast { idSeq++ } else { idLast = ms idSeq = 0 } idMu.Unlock() var out [26]byte // 时间部分:前 10 字符(48bit 毫秒,高位在前) t := uint64(ms) & ((1 << 48) - 1) for i := 9; i >= 0; i-- { out[i] = idChars[t&0x1f] t >>= 5 } // 随机部分:后 16 字符(80bit → 16×5bit) var rnd [10]byte if _, err := rand.Read(rnd[:]); err != nil { // 环境级致命错误:退化为时间+计数填充,不 panic 影响服务面 for i := range rnd { rnd[i] = byte(idSeq) ^ byte(i) } } var bits uint64 var nbits uint pos := 10 for _, b := range rnd { bits = bits<<8 | uint64(b) nbits += 8 for nbits >= 5 && pos < 26 { nbits -= 5 out[pos] = idChars[(bits>>nbits)&0x1f] pos++ } } if pos < 26 { out[25] = idChars[(bits<<(5-nbits))&0x1f] } return string(out[:]) }