From 0d124c0b7978253630ebddc152b2ffacc212c11c Mon Sep 17 00:00:00 2001 From: charlie <3140647@qq.com> Date: Thu, 26 Oct 2023 14:42:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0ID=E7=94=9F=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- idGenerator/assets/双缓存号段分配.webp | Bin 0 -> 19182 bytes idGenerator/buffer.go | 72 +++++++++ idGenerator/buffer_test.go | 25 ++++ idGenerator/formater.go | 132 +++++++++++++++++ idGenerator/formater_test.go | 34 +++++ idGenerator/generator.go | 124 ++++++++++++++++ idGenerator/genterator_test.go | 151 +++++++++++++++++++ idGenerator/id_generator.go | 118 --------------- idGenerator/id_generator_test.go | 32 ---- idGenerator/readme.md | 114 +++++++++++++++ idGenerator/store.go | 11 ++ idGenerator/store/mem_store.go | 60 ++++++++ idGenerator/store/mem_store_test.go | 21 +++ idGenerator/store/mysql_store.go | 2 + idGenerator/store/redis_store.go | 194 +++++++++++++++++++++++++ idGenerator/store/redis_store_test.go | 106 ++++++++++++++ idGenerator/store/segment.go | 33 +++++ 17 files changed, 1079 insertions(+), 150 deletions(-) create mode 100644 idGenerator/assets/双缓存号段分配.webp create mode 100644 idGenerator/buffer.go create mode 100644 idGenerator/buffer_test.go create mode 100644 idGenerator/formater.go create mode 100644 idGenerator/formater_test.go create mode 100644 idGenerator/generator.go create mode 100644 idGenerator/genterator_test.go delete mode 100644 idGenerator/id_generator.go delete mode 100644 idGenerator/id_generator_test.go create mode 100644 idGenerator/readme.md create mode 100644 idGenerator/store.go create mode 100644 idGenerator/store/mem_store.go create mode 100644 idGenerator/store/mem_store_test.go create mode 100644 idGenerator/store/mysql_store.go create mode 100644 idGenerator/store/redis_store.go create mode 100644 idGenerator/store/redis_store_test.go create mode 100644 idGenerator/store/segment.go diff --git a/idGenerator/assets/双缓存号段分配.webp b/idGenerator/assets/双缓存号段分配.webp new file mode 100644 index 0000000000000000000000000000000000000000..d04b30c32def871a19c599af014af0ef5ac6f849 GIT binary patch literal 19182 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 decimalMaxLength { + return nil, errors.New("the data length is out of limit") + } + + serialShift := int64(math.Pow10(serialLength)) + machineShift := int64(math.Pow10(machineLength)) + + return &decimalFormater{ + generator: generator{ + sequenceLength: serialShift, + maximalSequence: serialShift - 1, + machineCodeLength: machineShift, + maxinalMachineCode: machineShift - 1, + getTimestampFunc: func() int64 { + now := time.Now() + v := now.Format(format) + r, _ := strconv.ParseInt(v, 10, 64) + return r + }, + }, + supportReback: len(format) == 14, + }, nil +} + +func (f *decimalFormater) Format(machine, serial int64, reback bool) Id { + timestamp := f.getTimestamp(reback) + return Id(timestamp*f.sequenceLength*f.machineCodeLength + machine*f.sequenceLength + serial) +} diff --git a/idGenerator/formater_test.go b/idGenerator/formater_test.go new file mode 100644 index 0000000..764e4b4 --- /dev/null +++ b/idGenerator/formater_test.go @@ -0,0 +1,34 @@ +package idgenerator + +import ( + "testing" +) + +func TestBinary(t *testing.T) { + f, _ := newBinaryFormatter(DefaultStartTimeStamp, 16, 12) + t.Log(f.maxinalMachineCode, f.maximalSequence) +} + +func TestDecimal(t *testing.T) { + f, _ := newDecimalFormater(YYYYMMDDHHmmss, 4, 1) + t.Log(f.maxinalMachineCode, f.maxinalMachineCode) + t.Log(f.Format(22333, 9, false)) +} + +func TestDecimalMonth111(t *testing.T) { + f, _ := newDecimalFormater(YYYYMMDD, 4, 1) + t.Log(f.maxinalMachineCode, f.maxinalMachineCode) + + t.Log(f.Format(233, 9, false)) +} + +func TestBinaryTimestamp(t *testing.T) { + f, _ := newBinaryFormatter(DefaultStartTimeStamp, 10, 4) + for i := 0; i < 100; i++ { + if i%7 == 0 { + t.Log(f.Format(int64(i), 0xF, true)) + } else { + t.Log(f.Format(int64(i), 0xF, false)) + } + } +} diff --git a/idGenerator/generator.go b/idGenerator/generator.go new file mode 100644 index 0000000..ac9a8ee --- /dev/null +++ b/idGenerator/generator.go @@ -0,0 +1,124 @@ +package idgenerator + +import ( + "time" + + "github.com/charlienet/go-mixed/idGenerator/store" + "github.com/charlienet/go-mixed/mathx" + "github.com/charlienet/go-mixed/redis" +) + +const ( + defaultDoubleBufferStep int64 = 50 +) + +var DefaultStartTimeStamp = time.Date(2023, 1, 1, 0, 0, 0, 0, time.Local).Unix() + +type opt func(*idGenerator) error + +type Option struct { + TimeFormat string + SerialLength int + MachineLength int +} + +type idGenerator struct { + store storage // 外部存储 + formater formater // 格式化器 + buffer *doubleBuffer // 序列缓冲 +} + +func WithMem(machineCode int64) opt { + return WithStore(store.NewMemStore(machineCode)) +} + +func WithRedis(key string, rdb redis.Client) opt { + return WithStore(store.NewRedisStore(key, rdb)) +} + +func WithStore(s storage) opt { + return func(ig *idGenerator) error { + ig.store = s + return nil + } +} + +func WithDecimalFormater(format string, serialLength, machineLength int) opt { + return func(ig *idGenerator) error { + f, err := newDecimalFormater(format, serialLength, machineLength) + if err != nil { + return err + } + + ig.formater = f + return nil + } +} + +func WithBinaryFormatter(start int64, serialLength, machineLength int64) opt { + return func(ig *idGenerator) error { + f, err := newBinaryFormatter(start, serialLength, machineLength) + if err != nil { + return err + } + + ig.formater = f + return nil + } +} + +func New(opts ...opt) (*idGenerator, error) { + g := &idGenerator{} + + for _, o := range opts { + err := o(g) + if err != nil { + return nil, err + } + } + + if g.store == nil { + g.store = store.NewMemStore(0) + } + + _, err := g.store.UpdateMachineCode(g.formater.MaxinalMachineCode()) // 初始化机器码 + if err != nil { + return nil, err + } + + g.buffer = newDoubleBuffer(g.obtain) // 初始化序列缓冲 + + return g, nil +} + +func (g *idGenerator) WithRedis(key string, rdb redis.Client) *idGenerator { + return g.WithStore(store.NewRedisStore(key, rdb)) +} + +func (g *idGenerator) WithStore(s storage) *idGenerator { + g.store = s + return g +} + +func (g *idGenerator) Next() Id { + serial, reback := g.buffer.allot() + id := g.formater.Format(g.store.MachineCode(), serial, reback) + + return id +} + +func (g *idGenerator) Close() { + if g.store != nil { + g.store.Close() + } +} + +func (g *idGenerator) obtain() (*store.Segment, error) { + step := mathx.Min(defaultDoubleBufferStep, g.formater.MaximalSequence()) + s, err := g.store.Assign(0, g.formater.MaximalSequence(), step) + if err != nil { + println("分配失败", err.Error()) + } + + return s, err +} diff --git a/idGenerator/genterator_test.go b/idGenerator/genterator_test.go new file mode 100644 index 0000000..1080559 --- /dev/null +++ b/idGenerator/genterator_test.go @@ -0,0 +1,151 @@ +package idgenerator_test + +import ( + "sync" + "testing" + + idgenerator "github.com/charlienet/go-mixed/idGenerator" + "github.com/charlienet/go-mixed/redis" + "github.com/charlienet/go-mixed/sets" + "github.com/charlienet/go-mixed/tests" +) + +var redisOption = redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"} + +func TestGenerator(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + generator, err := idgenerator.New( + idgenerator.WithDecimalFormater(idgenerator.YYYYMMDDHHmmss, 1, 1), + idgenerator.WithRedis("idgen_test", rdb)) + if err != nil { + t.Fatal(err) + } + + for i := 0; i < 20; i++ { + t.Log(generator.Next()) + } + }) +} + +func TestDecimalGenerator(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + generator, err := idgenerator.New( + idgenerator.WithDecimalFormater(idgenerator.YYYYMMDDHHmmss, 3, 1), + idgenerator.WithRedis("idgen_test", rdb)) + if err != nil { + t.Fatal(err) + } + for i := 0; i < 200; i++ { + t.Log(generator.Next()) + } + + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"}) +} + +func TestDecimalMonth(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + generator, err := idgenerator.New( + idgenerator.WithDecimalFormater(idgenerator.YYYYMMDD, 2, 1), + idgenerator.WithRedis("idgen_test", rdb)) + if err != nil { + t.Fatal(err) + } + for i := 0; i < 105; i++ { + t.Log(generator.Next()) + } + + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"}) +} + +func TestParallelCreate(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + var wg sync.WaitGroup + + wg.Add(2) + go func() { + defer wg.Done() + + g1, err := idgenerator.New( + idgenerator.WithDecimalFormater(idgenerator.YYYYMMDDHHmmss, 3, 1), + idgenerator.WithRedis("idgen_testcccc", rdb)) + if err != nil { + panic(err) + } + + _ = g1.Next().Int64() + }() + + go func() { + defer wg.Done() + + g2, err := idgenerator.New( + idgenerator.WithDecimalFormater(idgenerator.YYYYMMDDHHmmss, 3, 1), + idgenerator.WithRedis("idgen_testcccc", rdb)) + if err != nil { + panic(err) + } + + _ = g2.Next().Int64() + }() + + wg.Wait() + + }, redisOption) +} + +func TestParallel(t *testing.T) { + set := sets.NewHashSet[int64]().Sync() + opt := redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"} + + _ = set + f := func() { + tests.RunOnRedis(t, func(rdb redis.Client) { + generator, err := idgenerator.New( + idgenerator.WithDecimalFormater(idgenerator.YYYYMMDDHHmmss, 3, 1), + idgenerator.WithRedis("idgen_testcccc", rdb)) + if err != nil { + t.Fatal(err) + } + defer generator.Close() + + generator.Next() + for i := 0; i < 50000; i++ { + id := generator.Next().Int64() + if set.Contains(id) { + panic("生成重复") + } + set.Add(id) + } + + }, opt) + } + + var wg sync.WaitGroup + for i := 0; i < 6; i++ { + wg.Add(1) + go func() { + defer wg.Done() + f() + }() + } + + wg.Wait() +} + +func BenchmarkGenerator(b *testing.B) { + tests.RunOnRedis(b, func(rdb redis.Client) { + b.Run("bbb", func(b *testing.B) { + generator, err := idgenerator.New( + idgenerator.WithDecimalFormater(idgenerator.YYYYMMDDHHmmss, 3, 1), + idgenerator.WithRedis("idgen_test", rdb)) + if err != nil { + b.Fatal(err) + } + + for i := 0; i < 999; i++ { + generator.Next() + } + + }) + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"}) +} diff --git a/idGenerator/id_generator.go b/idGenerator/id_generator.go deleted file mode 100644 index ce7f741..0000000 --- a/idGenerator/id_generator.go +++ /dev/null @@ -1,118 +0,0 @@ -package idgenerator - -import ( - "fmt" - "math" - "time" - - _ "unsafe" -) - -// 时间段开始时间 2022-01-01 -const startTimeStamp = 1640966400 - -const ( - MachineIdBits = uint(8) //机器id所占的位数 - SequenceBits = uint(12) //序列所占的位数 - MachineIdMax = int64(-1 ^ (-1 << MachineIdBits)) //支持的最大机器id数量 - SequenceMask = uint64(-1 ^ (-1 << SequenceBits)) // - MachineIdShift = uint64(SequenceBits) //机器id左移位数 - TimestampShift = uint64(SequenceBits + MachineIdBits) //时间戳左移位数 -) - -type TimePrecision int - -const ( - Second TimePrecision = iota // 秒 - Minute // 分 - Day // 日 -) - -type Config struct { - Machine int - TimeScope TimePrecision -} - -type Generator struct { - machine uint64 - timeScope TimePrecision // 时间段精度 - timestamp uint64 // 上次生成时间 - sequence uint64 // 上次使用序列 -} - -type Id struct { - machine uint64 // 机器标识 - Scope int // 时间精度 - Timestamp uint64 // 生成时间 - Sequence uint64 // 标识序列 -} - -func New(cfg Config) *Generator { - return &Generator{ - machine: uint64(cfg.Machine), - timeScope: cfg.TimeScope, - } -} - -func (g *Generator) Next() Id { - - now := currentTimestamp() - - if g.timestamp == now && g.sequence == 0 { - fmt.Println(time.Now().Format("2006-01-02 15:04:05.000"), "下一个时间点") - for now <= g.timestamp { - // runtime.Gosched() - now = currentTimestamp() - } - } - - g.timestamp = now // 标识生成时间 - g.sequence = (g.sequence + 1) & SequenceMask // 生成下一序列,超过最大值时返回零 - - return Id{ - machine: g.machine, - Timestamp: g.timestamp, - Sequence: g.sequence, - } -} - -func (i Id) Id() uint64 { - return i.Timestamp< max_seq,将分配上限提升一个步长 max_seq += step,并持久化 max_seq;重启时,读出持久化的 max_seq,赋值给 cur_seq。此种处理方式可以降低持久化的硬盘 IO 次数,可以系统的整体吞吐量。 + +2)分号段共享存储:引入号段 section 的概念,uin 相邻的一段用户属于一个号段,共享一个 max_seq。该处理方式可以大幅减少 max_seq 数据的大小,同时可以进一步地降低 IO 次数。 + + +二进制模式 +按照二进制模式处理序列号生成,各数据段按照各自的位长度组装 + +十进制模式 + + +缺陷 + +当发生序列回绕时,不同的时间序列有不同的处理。如时间序列精度为日,那么在发生回绕时认为此时的回绕不能发生。如果应用在当日内被重启,并且发生了回绕,当前机制无法检测此问题。 +序列记录可以依赖于外部存储,如Redis,DB等。当外部存储失效时序列可能会发生冲突。此时当时间段没有前进时可能发生重复。一般认为DB为不失效存储。 + + +需要烦精确的选择时间段和序列的相对关系,一般情况下 + + + + +ID分配器工作流程 + +初始化 +1. 创建生成器 +2. 创建外部存储 +3. 指定生成器生成规则,机器码长度,序列长度,序列化格式。 +4. 创建双缓存,使用外部存储分配数据段。 + + +标识分配 +1. 向序列缓冲器获取下一序列 +2. 向时间段分配器获取下一时间戳(传入是否回旋) +4. 由标识组装器获取 + +格式化器 + + 十进制格式化,时间段格式串 + 二进制格式化,起始时间,时间精度 \ No newline at end of file diff --git a/idGenerator/store.go b/idGenerator/store.go new file mode 100644 index 0000000..93152d3 --- /dev/null +++ b/idGenerator/store.go @@ -0,0 +1,11 @@ +package idgenerator + +import "github.com/charlienet/go-mixed/idGenerator/store" + +// 序列存储分配器 +type storage interface { + MachineCode() int64 // 当前机器码 + UpdateMachineCode(max int64) (int64, error) // 更新机器标识 + Assign(min, max, step int64) (*store.Segment, error) // 分配号段 + Close() +} diff --git a/idGenerator/store/mem_store.go b/idGenerator/store/mem_store.go new file mode 100644 index 0000000..a36c691 --- /dev/null +++ b/idGenerator/store/mem_store.go @@ -0,0 +1,60 @@ +package store + +import ( + "sync" + + "github.com/charlienet/go-mixed/mathx" +) + +type memStore struct { + mu sync.Mutex + machine int64 + current int64 +} + +func NewMemStore(machineCode int64) *memStore { + return &memStore{machine: machineCode} +} + +func (s *memStore) UpdateMachineCode(max int64) (int64, error) { + return s.machine, nil +} +func (s *memStore) MachineCode() int64 { + return s.machine +} + +func (s *memStore) Assign(min, max, step int64) (*Segment, error) { + s.mu.Lock() + defer s.mu.Unlock() + + step = mathx.Min(step, max) + start := mathx.Max(s.current, min) + end := start + step + + reback := false + if start >= max { + start = min + end = step + s.current = end + + reback = true + } + + if end > max { + end = max + s.current = end + } + + s.current = end + + return &Segment{ + start: start, + current: start, + end: end, + reback: reback, + }, nil +} + +func (s *memStore) Close() { + +} diff --git a/idGenerator/store/mem_store_test.go b/idGenerator/store/mem_store_test.go new file mode 100644 index 0000000..6e38beb --- /dev/null +++ b/idGenerator/store/mem_store_test.go @@ -0,0 +1,21 @@ +package store_test + +import ( + "testing" + + "github.com/charlienet/go-mixed/idGenerator/store" +) + +func TestMemSmall(t *testing.T) { + s := store.NewMemStore(2) + for i := 0; i < 10; i++ { + t.Log(s.Assign(1, 9, 20)) + } +} + +func TestMemBig(t *testing.T) { + s := store.NewMemStore(2) + for i := 0; i < 10; i++ { + t.Log(s.Assign(0, 99, 18)) + } +} diff --git a/idGenerator/store/mysql_store.go b/idGenerator/store/mysql_store.go new file mode 100644 index 0000000..3ed177b --- /dev/null +++ b/idGenerator/store/mysql_store.go @@ -0,0 +1,2 @@ +package store + diff --git a/idGenerator/store/redis_store.go b/idGenerator/store/redis_store.go new file mode 100644 index 0000000..79ae021 --- /dev/null +++ b/idGenerator/store/redis_store.go @@ -0,0 +1,194 @@ +package store + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "github.com/charlienet/go-mixed/rand" + "github.com/charlienet/go-mixed/redis" +) + +const ( + allocatedKey = "allocated" + sequenceKey = "sequence" + delaySecond = 10 +) + +const ( + // 机器码分配及保活,检查机器码键值是否匹配,如匹配成功对键进行延时。如不匹配需要重新申请机器码 + // 请求参数 机器码,机器识别码,机器码最大值 + machineLua = ` + local newKey = KEYS[1]..":"..ARGV[1] + if redis.call("GET", newKey) == ARGV[2] then + redis.call("EXPIRE", newKey, 10) + return tonumber(ARGV[1]) + else + for i = 0, ARGV[3], 1 do + newKey = KEYS[1]..":"..tostring(i) + + if redis.call("EXISTS", newKey) == 0 then + redis.call("SET", newKey, ARGV[2], "EX", 10) + return i + end + end + return -1 + end` + + // 序列分配min, max, step + segmentLua = ` +local key = KEYS[1] +local min = tonumber(ARGV[1]) +local max = tonumber(ARGV[2]) +local step = tonumber(ARGV[3]) + +if step > max then + step = max +end + +if redis.call("EXISTS", key) == 0 then + redis.call("SET", key, step) + return {min, step, 0} +end + +local begin = tonumber(redis.call("GET", key)) +local increase = redis.call("INCRBY", key, step) +local reback = 0 + +if begin >= max then + begin = min + increase = step + + redis.call("SET", key, step) + reback = 1 +end + +if increase > max then + increase = max + redis.call("SET", key, increase) +end + +return {begin, increase, reback} +` +) + +type redisStore struct { + rdb redis.Client + machinekey string // 机器码键 + sequenceKey string // 序列键 + value string // 随机键值 + machineCode int64 // 机器码 + close chan struct{} // 关闭保活协程 + isRunning bool // 是否已经关闭 + mu sync.Mutex +} + +func NewRedisStore(key string, rdb redis.Client) *redisStore { + return &redisStore{ + rdb: rdb, + machinekey: rdb.JoinKeys(key, allocatedKey), + sequenceKey: rdb.JoinKeys(key, sequenceKey), + value: rand.Hex.Generate(24), + close: make(chan struct{}), + } +} + +// 分配机器标识,分配值为-1时表示分配失败 +func (s *redisStore) UpdateMachineCode(max int64) (int64, error) { + err := s.updateMachine(max) + if err != nil { + return -1, err + } + + // 关闭原协程,开启新的保活协程 + + // if s.isRunning { + // s.close <- struct{}{} + // } + + // if !s.isRunning { + // s.close <- struct{}{} + go s.keepAlive(max) + // } + + return s.machineCode, nil +} + +func (s *redisStore) MachineCode() int64 { + return s.machineCode +} + +func (s *redisStore) Assign(min, max, step int64) (*Segment, error) { + s.mu.Lock() + defer s.mu.Unlock() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + + // 序列段分配 + key := s.rdb.JoinKeys(s.sequenceKey, fmt.Sprintf("%v", s.machineCode)) + r, err := s.rdb.Eval(ctx, segmentLua, []string{key}, min, max, step).Result() + if err != nil { + return &Segment{}, err + } + + start, end, reback := split(r) + return &Segment{start: start, end: end, current: start, reback: reback}, err +} + +func split(r any) (start, end int64, reback bool) { + if result, ok := r.([]any); ok { + start = result[0].(int64) + end = result[1].(int64) + reback = result[2].(int64) == 1 + } + + return +} + +func (s *redisStore) updateMachine(max int64) error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + r, err := s.rdb.Eval(ctx, machineLua, []string{s.machinekey}, s.machineCode, s.value, max).Result() + if err != nil { + return err + } + + if r == nil { + return errors.New("failed to obtain machine code") + } + + s.machineCode = r.(int64) + if s.machineCode == -1 { + return errors.New("machine code allocation failed") + } + + return nil +} + +func (s *redisStore) Close() { + s.close <- struct{}{} + s.isRunning = false +} + +func (s *redisStore) keepAlive(max int64) { + t := time.NewTicker(time.Second * (delaySecond / 3)) + defer t.Stop() + + for { + select { + case <-t.C: + // println("当前机器码:", s.machineCode) + err := s.updateMachine(max) + if err != nil { + fmt.Println("err:", err.Error()) + } + case <-s.close: + println("保活停止") + return + } + } +} diff --git a/idGenerator/store/redis_store_test.go b/idGenerator/store/redis_store_test.go new file mode 100644 index 0000000..965ee4c --- /dev/null +++ b/idGenerator/store/redis_store_test.go @@ -0,0 +1,106 @@ +package store_test + +import ( + "testing" + "time" + + "github.com/charlienet/go-mixed/idGenerator/store" + "github.com/charlienet/go-mixed/redis" + "github.com/charlienet/go-mixed/tests" +) + +func TestSmallSerail(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + s := store.NewRedisStore("sss", rdb) + for i := 0; i < 5; i++ { + t.Log(s.Assign(0, 9, 20)) + } + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"}) +} + +func TestSmallAssign(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + + s := store.NewRedisStore("sss", rdb) + + for i := 0; i < 10; i++ { + t.Log(s.Assign(0, 9, 30)) + } + + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"}) +} + +func TestBigAssign(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + + s := store.NewRedisStore("sss", rdb) + + for i := 0; i < 102; i++ { + t.Log(s.Assign(0, 99, 10)) + } + + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"}) +} + +func TestRedisAssign(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + + s := store.NewRedisStore("sss", rdb) + + for i := 0; i < 10; i++ { + t.Log(s.Assign(21, 99, 30)) + } + + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"}) +} + +func TestFullRedisAssign(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + + s := store.NewRedisStore("sss", rdb) + + for i := 0; i < 10; i++ { + t.Log(s.Assign(0, 999, 99)) + } + + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456"}) +} + +func TestUpdateMachineCode(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + + for i := 0; i < 20; i++ { + s := store.NewRedisStore("id", rdb) + code, err := s.UpdateMachineCode(99) + t.Log("获取到机器标识:", code, err) + + if err != nil { + return + } + + time.Sleep(time.Millisecond * 100) + + // s.Close() + } + + time.Sleep(time.Second * 10) + + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456", Prefix: "cacc"}) + +} + +func TestUpdate(t *testing.T) { + tests.RunOnRedis(t, func(rdb redis.Client) { + s := store.NewRedisStore("id", rdb) + s.UpdateMachineCode(99) + t.Log(s.MachineCode()) + + s.UpdateMachineCode(99) + t.Log(s.MachineCode()) + + s2 := store.NewRedisStore("id", rdb) + s2.UpdateMachineCode(99) + t.Log(s2.MachineCode()) + + }, redis.ReidsOption{Addr: "192.168.123.50:6379", Password: "123456", Prefix: "cacc"}) +} diff --git a/idGenerator/store/segment.go b/idGenerator/store/segment.go new file mode 100644 index 0000000..2307182 --- /dev/null +++ b/idGenerator/store/segment.go @@ -0,0 +1,33 @@ +package store + +import "fmt" + +// 号段 +type Segment struct { + start int64 + end int64 + current int64 + reback bool +} + +func (s *Segment) Allot() int64 { + s.current++ + return s.current +} + +func (s *Segment) IsEnding() bool { + return (s.current - s.start) > (s.end - s.current) +} + +func (s *Segment) IsEmpty() bool { + return s.current == s.end +} + +func (s *Segment) Reback() bool { + // println("回旋确认:", s.reback, s.current == (s.start+1)) + return s.reback && s.current == (s.start+1) +} + +func (s *Segment) String() string { + return fmt.Sprintf("start:%d-%d(%v)", s.start, s.end, s.reback) +}