voucher/internal/pkg/uid/generator.go

163 lines
3.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package uid
import (
"context"
"fmt"
"math"
"os"
"strconv"
"sync/atomic"
"time"
"github.com/redis/go-redis/v9"
)
const (
Order = 1
)
const (
BitsFull = 64 //
BitsPre = 1 // 固定
BitsTime = 41 // 毫秒时间戳 可以最多支持69年
BitsServer = 5 // 服务器最多支持32台
BitsWorker = 5 // 最多支持32种业务
BitsSequence = 12 // 一毫秒内支持4096个请求
OffsetTimestamp int64 = 1561910400000 // 时间戳起点时间毫秒 2019-07-01 00:00:00
)
type SignGenerator struct {
ServerID int
WorkerID int
RedisClient *redis.Client
}
func NewSignGenerator(redisClient *redis.Client) *SignGenerator {
return &SignGenerator{
RedisClient: redisClient,
}
}
func (sg *SignGenerator) GetNumber() (uint64, error) {
if sg.ServerID < 0 || sg.ServerID > 31 {
return 0, fmt.Errorf("serverID is lost")
}
if sg.WorkerID < 0 || sg.WorkerID > 31 {
return 0, fmt.Errorf("workerID is lost")
}
// redis报错等时最大5次retry就退出生成
maxRetryCount := 0
for {
if maxRetryCount > 5 {
return 0, fmt.Errorf("more than 5 times retry")
}
id := uint64(math.Pow(2, BitsFull-BitsPre)) << BitsPre
nowTime := time.Now().UnixNano() / int64(time.Millisecond)
startTime := OffsetTimestamp
diffTime := nowTime - startTime
shift := BitsFull - BitsPre - BitsTime
id |= uint64(diffTime << shift)
shift -= BitsServer
id |= uint64(sg.ServerID) << shift
shift -= BitsWorker
id |= uint64(sg.WorkerID) << shift
// 自增值
sequenceNumber := sg.getSequence(strconv.FormatUint(id, 10), 0)
if sequenceNumber > int64(math.Pow(2, BitsSequence)-1) {
time.Sleep(1 * time.Millisecond)
maxRetryCount++
} else {
id |= uint64(sequenceNumber)
return id, nil
}
}
}
func (sg *SignGenerator) GetServerID() int {
return sg.ServerID
}
func (sg *SignGenerator) SetServerID(serverID int) *SignGenerator {
sg.ServerID = serverID
return sg
}
func (sg *SignGenerator) GetWorkerID() int {
return sg.WorkerID
}
func (sg *SignGenerator) SetWorkerID(workerID int) *SignGenerator {
sg.WorkerID = workerID
return sg
}
func (sg *SignGenerator) getSequence(id string, level int) int64 {
// 嵌套大于50层时返回最大序号
if level > 50 {
return int64(math.Pow(2, BitsSequence)) + 1
}
lua := `
local sequenceKey = KEYS[1]
local sequenceNumber = redis.call("incr", sequenceKey)
redis.call("pexpire", sequenceKey, 100)
return sequenceNumber
`
sequence, err := sg.RedisClient.Eval(context.Background(), lua, []string{id}, 1).Int64()
if err != nil {
fmt.Println("SignGenerator err", sequence, err, level)
return sg.getSequence(id, level+1)
}
return sequence
}
func (sg *SignGenerator) ReverseNumber(number uint64) map[string]interface{} {
uuidItem := make(map[string]interface{})
shift := BitsFull - BitsPre - BitsTime
uuidItem["diffTime"] = (number >> shift) & (uint64(math.Pow(2, BitsTime)) - 1)
shift -= BitsServer
uuidItem["serverId"] = (number >> shift) & (uint64(math.Pow(2, BitsServer)) - 1)
shift -= BitsWorker
uuidItem["workerId"] = (number >> shift) & (uint64(math.Pow(2, BitsWorker)) - 1)
shift -= BitsSequence
uuidItem["sequenceNumber"] = (number >> shift) & (uint64(math.Pow(2, BitsSequence)) - 1)
timestamp := int64(uuidItem["diffTime"].(uint64)/1000) + (OffsetTimestamp / 1000)
uuidItem["generateTime"] = time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")
return uuidItem
}
var num int64
func GenerateNo() string {
t := time.Now()
s := t.Format("20060102150405")
m := t.UnixNano()/1e6 - t.UnixNano()/1e9*1e3
ms := sup(m, 3)
p := os.Getpid() % 1000
ps := sup(int64(p), 3)
i := atomic.AddInt64(&num, 1)
r := i % 10000
rs := sup(r, 4)
n := fmt.Sprintf("%s%s%s%s", s, ms, ps, rs)
return n
}
func sup(i int64, n int) string {
m := fmt.Sprintf("%d", i)
for len(m) < n {
m = fmt.Sprintf("0%s", m)
}
return m
}