From fad9b11582be518508921e9a647c397bfb3bbb91 Mon Sep 17 00:00:00 2001 From: zhaozhenhang Date: Sat, 18 Jun 2022 16:28:40 +0800 Subject: [PATCH 1/4] implement snowflake algorithm --- .../snowflake/sequencer_snowflake.go | 103 ++++++++++++++++++ .../snowflake/sequencer_snowflake_test.go | 25 +++++ 2 files changed, 128 insertions(+) create mode 100644 components/sequencer/snowflake/sequencer_snowflake.go create mode 100644 components/sequencer/snowflake/sequencer_snowflake_test.go diff --git a/components/sequencer/snowflake/sequencer_snowflake.go b/components/sequencer/snowflake/sequencer_snowflake.go new file mode 100644 index 0000000000..c1c16c2186 --- /dev/null +++ b/components/sequencer/snowflake/sequencer_snowflake.go @@ -0,0 +1,103 @@ +package snowflake + +import ( + "fmt" + "math/rand" + "sync" + "time" + + "mosn.io/pkg/log" +) + +var ( + machineRoomIdBits = 5 // 机房id 五位数 + machineIdBits = 5 // 机器id + sequencerBits = 12 // 每毫秒产生的id数量 + maxMachineRoomIdBit = int64(^(-1 << machineRoomIdBits)) // 当前机房id最大值 + maxMachineIdBit = int64(^(-1 << machineIdBits)) // 当前机器id最大值 + moveMachineRoomBit = sequencerBits + machineIdBits // 机房的左移动位 + moveMachineBit = sequencerBits // 机器的左移动位 + moveTimeStart = sequencerBits + machineIdBits + machineRoomIdBits // 时间戳的左移动位 + maxSequencerIdBit = ^(-1 << sequencerBits) // id 最大值 + machineRoomId int64 // 机房id + machineId int64 // 机器id + currentAQS = 0 // 并发控制 + lastTimestamp int64 = -1 //上次id产生的时间,防止时间回拨 + once sync.Once + instance *singleton + lock sync.Mutex + logger log.Logger +) + +const ( + timeStart = 1655540588 // 系统开始时间 +) + +type singleton struct { +} + +const ( + IP_PATTERN = "\\d{1,3}(\\.\\d{1,3}){3,5}$" //ip pattern +) + +// GetInstanceWM 获取singleton对象 单例模式 +func GetInstanceWM(machine, machineRoom int64) *singleton { + if machine > maxMachineIdBit || machine < 0 { + logger.Fatalf("the max lengths of the machine ID exceeds the maxMachineIdBit, maxMachineIdBit: %d ", maxMachineIdBit) + return nil + } + if machineRoom > maxMachineRoomIdBit || machineRoom < 0 { + logger.Fatalf("the max lengths of the machine room ID exceeds the maxMachineRoomIdBit, maxMachineIdBit: %d ", maxMachineIdBit) + return nil + } + once.Do(func() { + instance = &singleton{} + }) + machineId = int64(machine) + machineRoomId = int64(machineRoom) + return instance +} + +func (s *singleton) NextID() (int64, error) { + lock.Lock() + defer lock.Unlock() + timestamp := time.Now().Unix() // 这儿注意下,golang是否有效率问题 + if timestamp < lastTimestamp { + descTime := lastTimestamp - timestamp + if descTime < 5 { + // wait + time.Sleep(time.Duration(descTime<<1) * time.Millisecond) + timestamp = time.Now().Unix() + if timestamp < lastTimestamp { + return -1, fmt.Errorf("time moved backwards, refusing to generate id for %d milliseconds", descTime) + } + } else { + return -1, fmt.Errorf("time moved backwards, refusing to generate id for %d milliseconds", descTime) + } + } + if lastTimestamp == timestamp { + // 在同一毫秒内 + currentAQS = (currentAQS + 1) & maxSequencerIdBit + if currentAQS == 0 { + timestamp = tilNextMillis(lastTimestamp) + } + } else { + currentAQS = rand.Intn(2) + 1 + } + lastTimestamp = timestamp + nowTime := (timestamp - timeStart) << moveTimeStart + machineRoom := machineRoomId << moveMachineRoomBit + machine := machineId << moveMachineBit + return nowTime | machineRoom | machine, nil +} + +func tilNextMillis(lastTimestamp int64) int64 { + timestamp := time.Now().Unix() + for { + if timestamp <= lastTimestamp { + break + } + timestamp = time.Now().Unix() + } + return timestamp +} diff --git a/components/sequencer/snowflake/sequencer_snowflake_test.go b/components/sequencer/snowflake/sequencer_snowflake_test.go new file mode 100644 index 0000000000..b4fad4210f --- /dev/null +++ b/components/sequencer/snowflake/sequencer_snowflake_test.go @@ -0,0 +1,25 @@ +package snowflake + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetInstanceWM(t *testing.T) { + machine := int64(1) + machineRoom := int64(1) + s := GetInstanceWM(machine, machineRoom) + assert.NotNil(t, s) +} + +func TestSingleton_NextID(t *testing.T) { + s := GetInstanceWM(10, 10) + id, err := s.NextID() + if err != nil { + t.Errorf("error: %v", err) + } + logger.Printf("id : %d", id) + assert.NotNil(t, id) + +} From f4dd21a2568d1d186992818d580aac4bfbd270fa Mon Sep 17 00:00:00 2001 From: zhaozhenhang Date: Sat, 18 Jun 2022 16:56:16 +0800 Subject: [PATCH 2/4] implement snowflake algorithm --- components/sequencer/snowflake/sequencer_snowflake.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/sequencer/snowflake/sequencer_snowflake.go b/components/sequencer/snowflake/sequencer_snowflake.go index c1c16c2186..1e95a1c9c3 100644 --- a/components/sequencer/snowflake/sequencer_snowflake.go +++ b/components/sequencer/snowflake/sequencer_snowflake.go @@ -53,8 +53,8 @@ func GetInstanceWM(machine, machineRoom int64) *singleton { once.Do(func() { instance = &singleton{} }) - machineId = int64(machine) - machineRoomId = int64(machineRoom) + machineId = machine + machineRoomId = machineRoom return instance } From 675e164d5afccb8ed0c996c7f88e75962a819746 Mon Sep 17 00:00:00 2001 From: zhaozhenhang Date: Sat, 18 Jun 2022 16:56:56 +0800 Subject: [PATCH 3/4] implement snowflake algorithm --- .../sequencer/snowflake/sequencer_snowflake.go | 13 +++++++++++++ .../sequencer/snowflake/sequencer_snowflake_test.go | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/components/sequencer/snowflake/sequencer_snowflake.go b/components/sequencer/snowflake/sequencer_snowflake.go index 1e95a1c9c3..3ec35628d4 100644 --- a/components/sequencer/snowflake/sequencer_snowflake.go +++ b/components/sequencer/snowflake/sequencer_snowflake.go @@ -1,3 +1,16 @@ +// +// Copyright 2021 Layotto Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package snowflake import ( diff --git a/components/sequencer/snowflake/sequencer_snowflake_test.go b/components/sequencer/snowflake/sequencer_snowflake_test.go index b4fad4210f..797b77ea7d 100644 --- a/components/sequencer/snowflake/sequencer_snowflake_test.go +++ b/components/sequencer/snowflake/sequencer_snowflake_test.go @@ -1,3 +1,16 @@ +// +// Copyright 2021 Layotto Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package snowflake import ( From 1c2602b11424492a3807acdee4d6bf8e2bb00dc1 Mon Sep 17 00:00:00 2001 From: zhaozhenhang Date: Sat, 18 Jun 2022 17:06:03 +0800 Subject: [PATCH 4/4] implement snowflake algorithm --- components/sequencer/snowflake/sequencer_snowflake.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/sequencer/snowflake/sequencer_snowflake.go b/components/sequencer/snowflake/sequencer_snowflake.go index 3ec35628d4..8e6acbb9b1 100644 --- a/components/sequencer/snowflake/sequencer_snowflake.go +++ b/components/sequencer/snowflake/sequencer_snowflake.go @@ -37,7 +37,7 @@ var ( currentAQS = 0 // 并发控制 lastTimestamp int64 = -1 //上次id产生的时间,防止时间回拨 once sync.Once - instance *singleton + instance *Singleton lock sync.Mutex logger log.Logger ) @@ -46,7 +46,7 @@ const ( timeStart = 1655540588 // 系统开始时间 ) -type singleton struct { +type Singleton struct { } const ( @@ -54,7 +54,7 @@ const ( ) // GetInstanceWM 获取singleton对象 单例模式 -func GetInstanceWM(machine, machineRoom int64) *singleton { +func GetInstanceWM(machine, machineRoom int64) *Singleton { if machine > maxMachineIdBit || machine < 0 { logger.Fatalf("the max lengths of the machine ID exceeds the maxMachineIdBit, maxMachineIdBit: %d ", maxMachineIdBit) return nil @@ -64,14 +64,14 @@ func GetInstanceWM(machine, machineRoom int64) *singleton { return nil } once.Do(func() { - instance = &singleton{} + instance = &Singleton{} }) machineId = machine machineRoomId = machineRoom return instance } -func (s *singleton) NextID() (int64, error) { +func (s *Singleton) NextID() (int64, error) { lock.Lock() defer lock.Unlock() timestamp := time.Now().Unix() // 这儿注意下,golang是否有效率问题