mirror of
https://github.com/godruoyi/go-snowflake.git
synced 2026-01-05 11:44:55 +00:00
Compare commits
8 Commits
v0.0.1-bet
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
179ce18d3a | ||
|
|
fe9bfbc000 | ||
|
|
dfa375c97e | ||
|
|
0500e4b57b | ||
|
|
937cb414e9 | ||
|
|
495d927cce | ||
|
|
03bb06070a | ||
|
|
20079db81e |
8
.github/workflows/test.yaml
vendored
8
.github/workflows/test.yaml
vendored
@@ -12,13 +12,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.x
|
go-version: 1.17
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Run linters
|
- name: Run linters
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v3
|
||||||
with:
|
with:
|
||||||
version: v1.29
|
version: v1.29
|
||||||
|
|
||||||
|
|||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Godruoyi
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -18,7 +18,7 @@ func AtomicResolver(ms int64) (uint16, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if last == ms {
|
if last == ms {
|
||||||
seq = MaxSequence & (localSeq + 1)
|
seq = uint32(MaxSequence) & (localSeq + 1)
|
||||||
if seq == 0 {
|
if seq == 0 {
|
||||||
return MaxSequence, nil
|
return MaxSequence, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,18 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/godruoyi/go-snowflake"
|
"github.com/godruoyi/go-snowflake"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// set starttime and machineID for the first time if you wan't to use the default value
|
||||||
|
snowflake.SetStartTime(time.Date(2021, 9, 1, 0, 0, 0, 0, time.UTC))
|
||||||
|
snowflake.SetMachineID(snowflake.PrivateIPToMachineID()) // testing, not to be used in production
|
||||||
|
|
||||||
id := snowflake.ID()
|
id := snowflake.ID()
|
||||||
fmt.Println(id)
|
fmt.Println(id) // 1537200202186752
|
||||||
|
|
||||||
sid := snowflake.ParseID(id)
|
sid := snowflake.ParseID(id)
|
||||||
// SID {
|
// SID {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ So if you want use the snowflake algorithm to generate unique ID, You must ensur
|
|||||||
|
|
||||||
Based on this, we created this package and integrated multiple sequence-number providers into it.
|
Based on this, we created this package and integrated multiple sequence-number providers into it.
|
||||||
|
|
||||||
* AtomicResolver (base sync/atmoic)
|
* AtomicResolver (base sync/atomic)
|
||||||
|
|
||||||
> Each provider only needs to ensure that the serial number generated in the same millisecond is different. You can get a unique ID.
|
> Each provider only needs to ensure that the serial number generated in the same millisecond is different. You can get a unique ID.
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ Based on this, we created this package and integrated multiple sequence-number p
|
|||||||
$ go get github.com/godruoyi/go-snowflake
|
$ go get github.com/godruoyi/go-snowflake
|
||||||
```
|
```
|
||||||
|
|
||||||
## Useage
|
## Usage
|
||||||
|
|
||||||
1. simple to use.
|
1. simple to use.
|
||||||
|
|
||||||
|
|||||||
64
snowflake.go
64
snowflake.go
@@ -7,12 +7,12 @@ import (
|
|||||||
|
|
||||||
// These constants are the bit lengths of snowflake ID parts.
|
// These constants are the bit lengths of snowflake ID parts.
|
||||||
const (
|
const (
|
||||||
TimestampLength = 41
|
TimestampLength uint8 = 41
|
||||||
MachineIDLength = 10
|
MachineIDLength uint8 = 10
|
||||||
SequenceLength = 12
|
SequenceLength uint8 = 12
|
||||||
MaxSequence = 1<<SequenceLength - 1
|
MaxSequence uint16 = 1<<SequenceLength - 1
|
||||||
MaxTimestamp = 1<<TimestampLength - 1
|
MaxTimestamp uint64 = 1<<TimestampLength - 1
|
||||||
MaxMachineID = 1<<MachineIDLength - 1
|
MaxMachineID uint16 = 1<<MachineIDLength - 1
|
||||||
|
|
||||||
machineIDMoveLength = SequenceLength
|
machineIDMoveLength = SequenceLength
|
||||||
timestampMoveLength = MachineIDLength + SequenceLength
|
timestampMoveLength = MachineIDLength + SequenceLength
|
||||||
@@ -20,22 +20,22 @@ const (
|
|||||||
|
|
||||||
// SequenceResolver the snowflake sequence resolver.
|
// SequenceResolver the snowflake sequence resolver.
|
||||||
//
|
//
|
||||||
// When you want use the snowflake algorithm to generate unique ID, You must ensure: The sequence-number generated in the same millisecond of the same node is unique.
|
// When you want to use the snowflake algorithm to generate unique ID, You must ensure: The sequence-number generated in the same millisecond of the same node is unique.
|
||||||
// Based on this, we create this interface provide following reslover:
|
// Based on this, we create this interface provide following resolver:
|
||||||
// AtomicResolver : base sync/atomic (by default).
|
// AtomicResolver : base sync/atomic (by default).
|
||||||
type SequenceResolver func(ms int64) (uint16, error)
|
type SequenceResolver func(ms int64) (uint16, error)
|
||||||
|
|
||||||
// default start time is 2008-11-10 23:00:00 UTC, why ? In the playground the time begins at 2009-11-10 23:00:00 UTC.
|
// default start time is 2008-11-10 23:00:00 UTC, why ? In the playground the time begins at 2009-11-10 23:00:00 UTC.
|
||||||
// It's can run on golang playground.
|
// It can run on golang playground.
|
||||||
// default machineID is 0
|
// default machineID is 0
|
||||||
// default resolver is AtomicResolver
|
// default resolver is AtomicResolver
|
||||||
var (
|
var (
|
||||||
resolver SequenceResolver
|
resolver SequenceResolver
|
||||||
machineID = 0
|
machineID uint64 = 0
|
||||||
startTime = time.Date(2008, 11, 10, 23, 0, 0, 0, time.UTC)
|
startTime = time.Date(2008, 11, 10, 23, 0, 0, 0, time.UTC)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ID use ID to generate snowflake id and it will ignore error. if you want error info, you need use NextID method.
|
// ID use ID to generate snowflake id, and it will ignore error. if you want error info, you need use NextID method.
|
||||||
// This function is thread safe.
|
// This function is thread safe.
|
||||||
func ID() uint64 {
|
func ID() uint64 {
|
||||||
id, _ := NextID()
|
id, _ := NextID()
|
||||||
@@ -61,12 +61,12 @@ func NextID() (uint64, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
df := int(elapsedTime(c, startTime))
|
df := elapsedTime(c, startTime)
|
||||||
if df < 0 || df > MaxTimestamp {
|
if df < 0 || uint64(df) > MaxTimestamp {
|
||||||
return 0, errors.New("The maximum life cycle of the snowflake algorithm is 2^41-1(millis), please check starttime")
|
return 0, errors.New("the maximum life cycle of the snowflake algorithm is 2^41-1(millis), please check start-time")
|
||||||
}
|
}
|
||||||
|
|
||||||
id := uint64((df << timestampMoveLength) | (machineID << machineIDMoveLength) | int(seq))
|
id := (uint64(df) << uint64(timestampMoveLength)) | (machineID << uint64(machineIDMoveLength)) | uint64(seq)
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ func NextID() (uint64, error) {
|
|||||||
//
|
//
|
||||||
// It will panic when:
|
// It will panic when:
|
||||||
// s IsZero
|
// s IsZero
|
||||||
// s > current millisecond
|
// s > current millisecond,
|
||||||
// current millisecond - s > 2^41(69 years).
|
// current millisecond - s > 2^41(69 years).
|
||||||
// This function is thread-unsafe, recommended you call him in the main function.
|
// This function is thread-unsafe, recommended you call him in the main function.
|
||||||
func SetStartTime(s time.Time) {
|
func SetStartTime(s time.Time) {
|
||||||
@@ -84,29 +84,29 @@ func SetStartTime(s time.Time) {
|
|||||||
panic("The start time cannot be a zero value")
|
panic("The start time cannot be a zero value")
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.After(time.Now()) {
|
if s.After(time.Now().UTC()) {
|
||||||
panic("The s cannot be greater than the current millisecond")
|
panic("The s cannot be greater than the current millisecond")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Because s must after now, so the `df` not < 0.
|
// since we check the current millisecond is greater than s, so we don't need to check the overflow.
|
||||||
df := elapsedTime(currentMillis(), s)
|
df := elapsedTime(currentMillis(), s)
|
||||||
if df > MaxTimestamp {
|
if uint64(df) > MaxTimestamp {
|
||||||
panic("The maximum life cycle of the snowflake algorithm is 69 years")
|
panic("The maximum life cycle of the snowflake algorithm is 69 years")
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime = s
|
startTime = s
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMachineID specify the machine ID. It will panic when machineid > max limit for 2^10-1.
|
// SetMachineID specify the machine ID. It will panic when machined > max limit for 2^10-1.
|
||||||
// This function is thread-unsafe, recommended you call him in the main function.
|
// This function is thread-unsafe, recommended you call him in the main function.
|
||||||
func SetMachineID(m uint16) {
|
func SetMachineID(m uint16) {
|
||||||
if m > MaxMachineID {
|
if m > MaxMachineID {
|
||||||
panic("The machineid cannot be greater than 1023")
|
panic("The machineID cannot be greater than 1023")
|
||||||
}
|
}
|
||||||
machineID = int(m)
|
machineID = uint64(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSequenceResolver set an custom sequence resolver.
|
// SetSequenceResolver set a custom sequence resolver.
|
||||||
// This function is thread-unsafe, recommended you call him in the main function.
|
// This function is thread-unsafe, recommended you call him in the main function.
|
||||||
func SetSequenceResolver(seq SequenceResolver) {
|
func SetSequenceResolver(seq SequenceResolver) {
|
||||||
if seq != nil {
|
if seq != nil {
|
||||||
@@ -126,20 +126,20 @@ type SID struct {
|
|||||||
func (id *SID) GenerateTime() time.Time {
|
func (id *SID) GenerateTime() time.Time {
|
||||||
ms := startTime.UTC().UnixNano()/1e6 + int64(id.Timestamp)
|
ms := startTime.UTC().UnixNano()/1e6 + int64(id.Timestamp)
|
||||||
|
|
||||||
return time.Unix(0, (ms * int64(time.Millisecond))).UTC()
|
return time.Unix(0, ms*int64(time.Millisecond)).UTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseID parse snowflake it to SID struct.
|
// ParseID parse snowflake it to SID struct.
|
||||||
func ParseID(id uint64) SID {
|
func ParseID(id uint64) SID {
|
||||||
time := id >> (SequenceLength + MachineIDLength)
|
t := id >> uint64(SequenceLength+MachineIDLength)
|
||||||
sequence := id & MaxSequence
|
sequence := id & uint64(MaxSequence)
|
||||||
machineID := (id & (MaxMachineID << SequenceLength)) >> SequenceLength
|
mID := (id & (uint64(MaxMachineID) << SequenceLength)) >> SequenceLength
|
||||||
|
|
||||||
return SID{
|
return SID{
|
||||||
ID: id,
|
ID: id,
|
||||||
Sequence: sequence,
|
Sequence: sequence,
|
||||||
MachineID: machineID,
|
MachineID: mID,
|
||||||
Timestamp: time,
|
Timestamp: t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,8 +163,8 @@ func callSequenceResolver() SequenceResolver {
|
|||||||
return resolver
|
return resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func elapsedTime(nowms int64, s time.Time) int64 {
|
func elapsedTime(noms int64, s time.Time) int64 {
|
||||||
return nowms - s.UTC().UnixNano()/1e6
|
return noms - s.UTC().UnixNano()/1e6
|
||||||
}
|
}
|
||||||
|
|
||||||
// currentMillis get current millisecond.
|
// currentMillis get current millisecond.
|
||||||
|
|||||||
@@ -156,8 +156,8 @@ func TestSetMachineID(t *testing.T) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err == nil {
|
if err := recover(); err == nil {
|
||||||
tt.Error("Should throw a error")
|
tt.Error("Should throw a error")
|
||||||
} else if err.(string) != "The machineid cannot be greater than 1023" {
|
} else if err.(string) != "The machineID cannot be greater than 1023" {
|
||||||
tt.Error("The error message should be eq 「The machineid cannot be greater than 1023」")
|
tt.Error("The error message should be eq 「The machineID cannot be greater than 1023」")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user