├── idgen ├── ISnowWorker.go ├── IIdGenerator.go ├── IdGeneratorException.go ├── OverCostActionArg.go ├── YitIdHelper.go ├── SnowWorkerM2.go ├── IdGeneratorOptions.go ├── DefaultIdGenerator.go └── SnowWorkerM1.go ├── go.mod ├── .github └── workflows │ └── go.yml ├── .travis.yml ├── README.md ├── LICENSE ├── main.go ├── reg.go ├── .gitignore ├── go.sum └── regworkerid └── reghelper.go /idgen/ISnowWorker.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 版权属于:yitter(yitter@126.com) 3 | * 代码编辑:guoyahao 4 | * 代码修订:yitter 5 | * 开源地址:https://github.com/yitter/idgenerator 6 | */ 7 | 8 | package idgen 9 | 10 | type ISnowWorker interface { 11 | NextId() int64 12 | } 13 | -------------------------------------------------------------------------------- /idgen/IIdGenerator.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 版权属于:yitter(yitter@126.com) 3 | * 代码编辑:guoyahao 4 | * 代码修订:yitter 5 | * 开源地址:https://github.com/yitter/idgenerator 6 | */ 7 | 8 | package idgen 9 | 10 | type IIdGenerator interface { 11 | NewLong() uint64 12 | } 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yitter/idgenerator-go 2 | 3 | go 1.17 4 | 5 | require github.com/go-redis/redis/v8 v8.11.5 6 | 7 | require ( 8 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 9 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /idgen/IdGeneratorException.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 版权属于:yitter(yitter@126.com) 3 | * 代码编辑:guoyahao 4 | * 代码修订:yitter 5 | * 开源地址:https://github.com/yitter/idgenerator 6 | */ 7 | 8 | // Package idgen is a snowflake id generator 9 | package idgen 10 | 11 | import "fmt" 12 | 13 | type IdGeneratorException struct { 14 | message string 15 | error error 16 | } 17 | 18 | func (e IdGeneratorException) IdGeneratorException(message ...interface{}) { 19 | fmt.Println(message...) 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: idgenerator-go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | matrix: 4 | fast_finish: true 5 | include: 6 | - go: 1.17.x 7 | - go: 1.18.x 8 | - go: master 9 | 10 | before_install: 11 | - if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi 12 | - if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi 13 | 14 | install: 15 | - go get -v . 16 | #- go get -v golang.org/x/lint/golint 17 | 18 | script: 19 | - diff <(gofmt -d .) <(echo -n) 20 | - go vet -x ./... 21 | #- golint -set_exit_status ./... 22 | - GOARCH=amd64 go test -v ./... -coverprofile=coverage.txt -covermode=atomic 23 | - go run ./main.go 24 | 25 | after_success: 26 | - bash <(curl -s https://codecov.io/bash) 27 | -------------------------------------------------------------------------------- /idgen/OverCostActionArg.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 版权属于:yitter(yitter@126.com) 3 | * 代码编辑:guoyahao 4 | * 代码修订:yitter 5 | * 开源地址:https://github.com/yitter/idgenerator 6 | */ 7 | 8 | package idgen 9 | 10 | type OverCostActionArg struct { 11 | ActionType int32 12 | TimeTick int64 13 | WorkerId uint16 14 | OverCostCountInOneTerm int32 15 | GenCountInOneTerm int32 16 | TermIndex int32 17 | } 18 | 19 | func (ocaa OverCostActionArg) OverCostActionArg(workerId uint16, timeTick int64, actionType int32, overCostCountInOneTerm int32, genCountWhenOverCost int32, index int32) { 20 | ocaa.ActionType = actionType 21 | ocaa.TimeTick = timeTick 22 | ocaa.WorkerId = workerId 23 | ocaa.OverCostCountInOneTerm = overCostCountInOneTerm 24 | ocaa.GenCountInOneTerm = genCountWhenOverCost 25 | ocaa.TermIndex = index 26 | } 27 | -------------------------------------------------------------------------------- /idgen/YitIdHelper.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 版权属于:yitter(yitter@126.com) 3 | * 代码编辑:guoyahao 4 | * 代码修订:yitter 5 | * 开源地址:https://github.com/yitter/idgenerator 6 | */ 7 | 8 | package idgen 9 | 10 | import ( 11 | "sync" 12 | "time" 13 | ) 14 | 15 | var singletonMutex sync.Mutex 16 | var idGenerator *DefaultIdGenerator 17 | 18 | // SetIdGenerator . 19 | func SetIdGenerator(options *IdGeneratorOptions) { 20 | singletonMutex.Lock() 21 | idGenerator = NewDefaultIdGenerator(options) 22 | singletonMutex.Unlock() 23 | } 24 | 25 | // NextId . 26 | func NextId() int64 { 27 | // if idGenerator == nil { 28 | // singletonMutex.Lock() 29 | // defer singletonMutex.Unlock() 30 | // if idGenerator == nil { 31 | // options := NewIdGeneratorOptions(1) 32 | // idGenerator = NewDefaultIdGenerator(options) 33 | // } 34 | // } 35 | 36 | return idGenerator.NewLong() 37 | } 38 | 39 | func ExtractTime(id int64) time.Time { 40 | return idGenerator.ExtractTime(id) 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ❄ idgenerator-Go 2 | 3 | ## 介绍 4 | 项目更多介绍参照:https://github.com/yitter/idgenerator 5 | 6 | ## 引用全局库 7 | ``` 8 | go get -u -v github.com/yitter/idgenerator-go@v1.3.1 9 | ``` 10 | 11 | ## 调用示例(Go) 12 | 13 | 第1步,**全局** 初始化(应用程序启动时执行一次): 14 | ``` 15 | // 创建 IdGeneratorOptions 对象,请在构造函数中输入 WorkerId: 16 | var options = idgen.NewIdGeneratorOptions(1) 17 | // options.WorkerIdBitLength = 10 // WorkerIdBitLength 默认值6,支持的 WorkerId 最大值为2^6-1,若 WorkerId 超过64,可设置更大的 WorkerIdBitLength 18 | // ...... 其它参数设置参考 IdGeneratorOptions 定义,一般来说,只要再设置 WorkerIdBitLength (决定 WorkerId 的最大值)。 19 | 20 | // 保存参数(必须的操作,否则以上设置都不能生效): 21 | idgen.SetIdGenerator(options) 22 | // 以上初始化过程只需全局一次,且必须在第2步之前设置。 23 | ``` 24 | 25 | 第2步,生成ID: 26 | ``` 27 | // 初始化以后,即可在任何需要生成ID的地方,调用以下方法: 28 | var newId = idgen.NextId() 29 | ``` 30 | 31 | ## 关于Go环境 32 | 33 | 1.SDK,go1.17 34 | 35 | 2.启用 Go-Modules 36 | ``` 37 | go env -w GO111MODULE=on 38 | 39 | # Next *ONLY* for China-Users: 40 | go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct 41 | ``` 42 | 43 | ## 代码贡献者(按时间顺序) 44 | guoyahao | amuluowin | houseme 45 | 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 yitter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /idgen/SnowWorkerM2.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 版权属于:yitter(yitter@126.com) 3 | * 代码编辑:guoyahao 4 | * 代码修订:yitter 5 | * 开源地址:https://github.com/yitter/idgenerator 6 | */ 7 | 8 | package idgen 9 | 10 | import ( 11 | "fmt" 12 | "strconv" 13 | ) 14 | 15 | type SnowWorkerM2 struct { 16 | *SnowWorkerM1 17 | } 18 | 19 | func NewSnowWorkerM2(options *IdGeneratorOptions) ISnowWorker { 20 | return &SnowWorkerM2{ 21 | NewSnowWorkerM1(options).(*SnowWorkerM1), 22 | } 23 | } 24 | 25 | func (m2 SnowWorkerM2) NextId() int64 { 26 | m2.Lock() 27 | defer m2.Unlock() 28 | currentTimeTick := m2.GetCurrentTimeTick() 29 | if m2._LastTimeTick == currentTimeTick { 30 | m2._CurrentSeqNumber++ 31 | if m2._CurrentSeqNumber > m2.MaxSeqNumber { 32 | m2._CurrentSeqNumber = m2.MinSeqNumber 33 | currentTimeTick = m2.GetNextTimeTick() 34 | } 35 | } else { 36 | m2._CurrentSeqNumber = m2.MinSeqNumber 37 | } 38 | if currentTimeTick < m2._LastTimeTick { 39 | fmt.Println("Time error for {0} milliseconds", strconv.FormatInt(m2._LastTimeTick-currentTimeTick, 10)) 40 | } 41 | m2._LastTimeTick = currentTimeTick 42 | result := int64(currentTimeTick< time.Now().UnixNano()/1e6 { 29 | panic("BaseTime error.") 30 | } 31 | 32 | // 2.WorkerIdBitLength 33 | if options.WorkerIdBitLength <= 0 { 34 | panic("WorkerIdBitLength error.(range:[1, 21])") 35 | } 36 | if options.WorkerIdBitLength+options.SeqBitLength > 22 { 37 | panic("error:WorkerIdBitLength + SeqBitLength <= 22") 38 | } 39 | 40 | // 3.WorkerId 41 | maxWorkerIdNumber := uint16(1< maxWorkerIdNumber { 46 | panic("WorkerId error. (range:[0, " + strconv.FormatUint(uint64(maxWorkerIdNumber), 10) + "]") 47 | } 48 | 49 | // 4.SeqBitLength 50 | if options.SeqBitLength < 2 || options.SeqBitLength > 21 { 51 | panic("SeqBitLength error. (range:[2, 21])") 52 | } 53 | 54 | // 5.MaxSeqNumber 55 | maxSeqNumber := uint32(1< maxSeqNumber { 60 | panic("MaxSeqNumber error. (range:[1, " + strconv.FormatUint(uint64(maxSeqNumber), 10) + "]") 61 | } 62 | 63 | // 6.MinSeqNumber 64 | if options.MinSeqNumber < 5 || options.MinSeqNumber > maxSeqNumber { 65 | panic("MinSeqNumber error. (range:[5, " + strconv.FormatUint(uint64(maxSeqNumber), 10) + "]") 66 | } 67 | 68 | // 7.TopOverCostCount 69 | if options.TopOverCostCount < 0 || options.TopOverCostCount > 10000 { 70 | panic("TopOverCostCount error. (range:[0, 10000]") 71 | } 72 | 73 | var snowWorker ISnowWorker 74 | switch options.Method { 75 | case 1: 76 | snowWorker = NewSnowWorkerM1(options) 77 | case 2: 78 | snowWorker = NewSnowWorkerM2(options) 79 | default: 80 | snowWorker = NewSnowWorkerM1(options) 81 | } 82 | 83 | if options.Method == 1 { 84 | time.Sleep(time.Duration(500) * time.Microsecond) 85 | } 86 | 87 | return &DefaultIdGenerator{ 88 | Options: options, 89 | SnowWorker: snowWorker, 90 | } 91 | } 92 | 93 | func (dig DefaultIdGenerator) NewLong() int64 { 94 | return dig.SnowWorker.NextId() 95 | } 96 | 97 | func (dig DefaultIdGenerator) ExtractTime(id int64) time.Time { 98 | return time.UnixMilli(id>>(dig.Options.WorkerIdBitLength+dig.Options.SeqBitLength) + dig.Options.BaseTime) 99 | } 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | *.editorconfig 10 | .vscode 11 | __commit.bat 12 | __download.bat 13 | target 14 | .dccache 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | [Ll]og/ 30 | 31 | # Visual Studio 2015 cache/options directory 32 | **/.vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # DNX 50 | project.lock.json 51 | artifacts/ 52 | 53 | *_i.c 54 | *_p.c 55 | *_i.h 56 | *.ilk 57 | *.meta 58 | *.obj 59 | *.pch 60 | *.pdb 61 | *.pgc 62 | *.pgd 63 | *.rsp 64 | *.sbr 65 | *.tlb 66 | *.tli 67 | *.tlh 68 | *.tmp 69 | *.tmp_proj 70 | *.log 71 | *.vspscc 72 | *.vssscc 73 | .builds 74 | *.pidb 75 | *.svclog 76 | *.scc 77 | 78 | # Chutzpah Test files 79 | _Chutzpah* 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opendb 86 | *.opensdf 87 | *.sdf 88 | *.cachefile 89 | *.VC.db 90 | *.VC.VC.opendb 91 | 92 | # Visual Studio profiler 93 | *.psess 94 | *.vsp 95 | *.vspx 96 | *.sap 97 | 98 | # TFS 2012 Local Workspace 99 | $tf/ 100 | 101 | # Guidance Automation Toolkit 102 | *.gpState 103 | 104 | # ReSharper is a .NET coding add-in 105 | _ReSharper*/ 106 | *.[Rr]e[Ss]harper 107 | *.DotSettings.user 108 | 109 | # JustCode is a .NET coding add-in 110 | .JustCode 111 | 112 | # TeamCity is a build add-in 113 | _TeamCity* 114 | 115 | # DotCover is a Code Coverage Tool 116 | *.dotCover 117 | 118 | # NCrunch 119 | _NCrunch_* 120 | .*crunch*.local.xml 121 | nCrunchTemp_* 122 | 123 | # MightyMoose 124 | *.mm.* 125 | AutoTest.Net/ 126 | 127 | # Web workbench (sass) 128 | .sass-cache/ 129 | 130 | # Installshield output folder 131 | [Ee]xpress/ 132 | 133 | # DocProject is a documentation generator add-in 134 | DocProject/buildhelp/ 135 | DocProject/Help/*.HxT 136 | DocProject/Help/*.HxC 137 | DocProject/Help/*.hhc 138 | DocProject/Help/*.hhk 139 | DocProject/Help/*.hhp 140 | DocProject/Help/Html2 141 | DocProject/Help/html 142 | 143 | # Click-Once directory 144 | publish/ 145 | 146 | # Publish Web Output 147 | *.[Pp]ublish.xml 148 | *.azurePubxml 149 | # TODO: Comment the next line if you want to checkin your web deploy settings 150 | # but database connection strings (with potential passwords) will be unencrypted 151 | *.pubxml 152 | *.publishproj 153 | 154 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 155 | # checkin your Azure Web App publish settings, but sensitive information contained 156 | # in these scripts will be unencrypted 157 | PublishScripts/ 158 | 159 | # NuGet Packages 160 | *.nupkg 161 | *.snupkg 162 | # The packages folder can be ignored because of Package Restore 163 | **/packages/* 164 | # except build/, which is used as an MSBuild target. 165 | !**/packages/build/ 166 | # Uncomment if necessary however generally it will be regenerated when needed 167 | #!**/packages/repositories.config 168 | # NuGet v3's project.json files produces more ignoreable files 169 | *.nuget.props 170 | *.nuget.targets 171 | 172 | # Microsoft Azure Build Output 173 | csx/ 174 | *.build.csdef 175 | 176 | # Microsoft Azure Emulator 177 | ecf/ 178 | rcf/ 179 | 180 | # Windows Store app package directories and files 181 | AppPackages/ 182 | BundleArtifacts/ 183 | Package.StoreAssociation.xml 184 | _pkginfo.txt 185 | 186 | # Visual Studio cache files 187 | # files ending in .cache can be ignored 188 | *.[Cc]ache 189 | # but keep track of directories ending in .cache 190 | !*.[Cc]ache/ 191 | 192 | # Others 193 | ClientBin/ 194 | ~$* 195 | *~ 196 | *.dbmdl 197 | *.dbproj.schemaview 198 | *.pfx 199 | *.publishsettings 200 | node_modules/ 201 | orleans.codegen.cs 202 | 203 | # Since there are multiple workflows, uncomment next line to ignore bower_components 204 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 205 | #bower_components/ 206 | 207 | # RIA/Silverlight projects 208 | Generated_Code/ 209 | 210 | # Backup & report files from converting an old project file 211 | # to a newer Visual Studio version. Backup files are not needed, 212 | # because we have git ;-) 213 | _UpgradeReport_Files/ 214 | Backup*/ 215 | UpgradeLog*.XML 216 | UpgradeLog*.htm 217 | 218 | # SQL Server files 219 | *.mdf 220 | *.ldf 221 | 222 | # Business Intelligence projects 223 | *.rdl.data 224 | *.bim.layout 225 | *.bim_*.settings 226 | 227 | # Microsoft Fakes 228 | FakesAssemblies/ 229 | 230 | # GhostDoc plugin setting file 231 | *.GhostDoc.xml 232 | 233 | # Node.js Tools for Visual Studio 234 | .ntvs_analysis.dat 235 | 236 | # Visual Studio 6 build log 237 | *.plg 238 | 239 | # Visual Studio 6 workspace options file 240 | *.opt 241 | 242 | # Visual Studio LightSwitch build output 243 | **/*.HTMLClient/GeneratedArtifacts 244 | **/*.DesktopClient/GeneratedArtifacts 245 | **/*.DesktopClient/ModelManifest.xml 246 | **/*.Server/GeneratedArtifacts 247 | **/*.Server/ModelManifest.xml 248 | _Pvt_Extensions 249 | 250 | # Paket dependency manager 251 | .paket/paket.exe 252 | paket-files/ 253 | 254 | # FAKE - F# Make 255 | .fake/ 256 | 257 | # JetBrains Rider 258 | .idea/ 259 | *.sln.iml 260 | 261 | 262 | # macOS 263 | .DS_Store 264 | -------------------------------------------------------------------------------- /idgen/SnowWorkerM1.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 版权属于:yitter(yitter@126.com) 3 | * 代码编辑:guoyahao 4 | * 代码修订:yitter 5 | * 开源地址:https://github.com/yitter/idgenerator 6 | */ 7 | 8 | package idgen 9 | 10 | import ( 11 | "sync" 12 | "time" 13 | ) 14 | 15 | // SnowWorkerM1 . 16 | type SnowWorkerM1 struct { 17 | BaseTime int64 // 基础时间 18 | WorkerId uint16 // 机器码 19 | WorkerIdBitLength byte // 机器码位长 20 | SeqBitLength byte // 自增序列数位长 21 | MaxSeqNumber uint32 // 最大序列数(含) 22 | MinSeqNumber uint32 // 最小序列数(含) 23 | TopOverCostCount uint32 // 最大漂移次数 24 | _TimestampShift byte 25 | _CurrentSeqNumber uint32 26 | 27 | _LastTimeTick int64 28 | _TurnBackTimeTick int64 29 | _TurnBackIndex byte 30 | _IsOverCost bool 31 | _OverCostCountInOneTerm uint32 32 | // _GenCountInOneTerm uint32 33 | // _TermIndex uint32 34 | 35 | sync.Mutex 36 | } 37 | 38 | // NewSnowWorkerM1 . 39 | func NewSnowWorkerM1(options *IdGeneratorOptions) ISnowWorker { 40 | var workerIdBitLength byte 41 | var seqBitLength byte 42 | var maxSeqNumber uint32 43 | 44 | // 1.BaseTime 45 | var baseTime int64 46 | if options.BaseTime != 0 { 47 | baseTime = options.BaseTime 48 | } else { 49 | baseTime = 1582136402000 50 | } 51 | 52 | // 2.WorkerIdBitLength 53 | if options.WorkerIdBitLength == 0 { 54 | workerIdBitLength = 6 55 | } else { 56 | workerIdBitLength = options.WorkerIdBitLength 57 | } 58 | 59 | // 3.WorkerId 60 | var workerId = options.WorkerId 61 | 62 | // 4.SeqBitLength 63 | if options.SeqBitLength == 0 { 64 | seqBitLength = 6 65 | } else { 66 | seqBitLength = options.SeqBitLength 67 | } 68 | 69 | // 5.MaxSeqNumber 70 | if options.MaxSeqNumber <= 0 { 71 | maxSeqNumber = (1 << seqBitLength) - 1 72 | } else { 73 | maxSeqNumber = options.MaxSeqNumber 74 | } 75 | 76 | // 6.MinSeqNumber 77 | var minSeqNumber = options.MinSeqNumber 78 | 79 | // 7.Others 80 | var topOverCostCount = options.TopOverCostCount 81 | if topOverCostCount == 0 { 82 | topOverCostCount = 2000 83 | } 84 | 85 | timestampShift := (byte)(workerIdBitLength + seqBitLength) 86 | currentSeqNumber := minSeqNumber 87 | 88 | return &SnowWorkerM1{ 89 | BaseTime: baseTime, 90 | WorkerIdBitLength: workerIdBitLength, 91 | WorkerId: workerId, 92 | SeqBitLength: seqBitLength, 93 | MaxSeqNumber: maxSeqNumber, 94 | MinSeqNumber: minSeqNumber, 95 | TopOverCostCount: topOverCostCount, 96 | _TimestampShift: timestampShift, 97 | _CurrentSeqNumber: currentSeqNumber, 98 | 99 | _LastTimeTick: 0, 100 | _TurnBackTimeTick: 0, 101 | _TurnBackIndex: 0, 102 | _IsOverCost: false, 103 | _OverCostCountInOneTerm: 0, 104 | // _GenCountInOneTerm: 0, 105 | // _TermIndex: 0, 106 | } 107 | } 108 | 109 | // DoGenIDAction . 110 | func (m1 *SnowWorkerM1) DoGenIdAction(arg *OverCostActionArg) { 111 | 112 | } 113 | 114 | func (m1 *SnowWorkerM1) BeginOverCostAction(useTimeTick int64) { 115 | 116 | } 117 | 118 | func (m1 *SnowWorkerM1) EndOverCostAction(useTimeTick int64) { 119 | // if m1._TermIndex > 10000 { 120 | // m1._TermIndex = 0 121 | // } 122 | } 123 | 124 | func (m1 *SnowWorkerM1) BeginTurnBackAction(useTimeTick int64) { 125 | 126 | } 127 | 128 | func (m1 *SnowWorkerM1) EndTurnBackAction(useTimeTick int64) { 129 | 130 | } 131 | 132 | func (m1 *SnowWorkerM1) NextOverCostId() int64 { 133 | currentTimeTick := m1.GetCurrentTimeTick() 134 | if currentTimeTick > m1._LastTimeTick { 135 | // m1.EndOverCostAction(currentTimeTick) 136 | m1._LastTimeTick = currentTimeTick 137 | m1._CurrentSeqNumber = m1.MinSeqNumber 138 | m1._IsOverCost = false 139 | m1._OverCostCountInOneTerm = 0 140 | // m1._GenCountInOneTerm = 0 141 | return m1.CalcId(m1._LastTimeTick) 142 | } 143 | if m1._OverCostCountInOneTerm >= m1.TopOverCostCount { 144 | // m1.EndOverCostAction(currentTimeTick) 145 | m1._LastTimeTick = m1.GetNextTimeTick() 146 | m1._CurrentSeqNumber = m1.MinSeqNumber 147 | m1._IsOverCost = false 148 | m1._OverCostCountInOneTerm = 0 149 | // m1._GenCountInOneTerm = 0 150 | return m1.CalcId(m1._LastTimeTick) 151 | } 152 | if m1._CurrentSeqNumber > m1.MaxSeqNumber { 153 | m1._LastTimeTick++ 154 | m1._CurrentSeqNumber = m1.MinSeqNumber 155 | m1._IsOverCost = true 156 | m1._OverCostCountInOneTerm++ 157 | // m1._GenCountInOneTerm++ 158 | 159 | return m1.CalcId(m1._LastTimeTick) 160 | } 161 | 162 | // m1._GenCountInOneTerm++ 163 | return m1.CalcId(m1._LastTimeTick) 164 | } 165 | 166 | // NextNormalID . 167 | func (m1 *SnowWorkerM1) NextNormalId() int64 { 168 | currentTimeTick := m1.GetCurrentTimeTick() 169 | if currentTimeTick < m1._LastTimeTick { 170 | if m1._TurnBackTimeTick < 1 { 171 | m1._TurnBackTimeTick = m1._LastTimeTick - 1 172 | m1._TurnBackIndex++ 173 | // 每毫秒序列数的前5位是预留位,0用于手工新值,1-4是时间回拨次序 174 | // 支持4次回拨次序(避免回拨重叠导致ID重复),可无限次回拨(次序循环使用)。 175 | if m1._TurnBackIndex > 4 { 176 | m1._TurnBackIndex = 1 177 | } 178 | m1.BeginTurnBackAction(m1._TurnBackTimeTick) 179 | } 180 | 181 | // time.Sleep(time.Duration(1) * time.Millisecond) 182 | return m1.CalcTurnBackId(m1._TurnBackTimeTick) 183 | } 184 | 185 | // 时间追平时,_TurnBackTimeTick清零 186 | if m1._TurnBackTimeTick > 0 { 187 | m1.EndTurnBackAction(m1._TurnBackTimeTick) 188 | m1._TurnBackTimeTick = 0 189 | } 190 | 191 | if currentTimeTick > m1._LastTimeTick { 192 | m1._LastTimeTick = currentTimeTick 193 | m1._CurrentSeqNumber = m1.MinSeqNumber 194 | return m1.CalcId(m1._LastTimeTick) 195 | } 196 | 197 | if m1._CurrentSeqNumber > m1.MaxSeqNumber { 198 | m1.BeginOverCostAction(currentTimeTick) 199 | // m1._TermIndex++ 200 | m1._LastTimeTick++ 201 | m1._CurrentSeqNumber = m1.MinSeqNumber 202 | m1._IsOverCost = true 203 | m1._OverCostCountInOneTerm = 1 204 | // m1._GenCountInOneTerm = 1 205 | 206 | return m1.CalcId(m1._LastTimeTick) 207 | } 208 | 209 | return m1.CalcId(m1._LastTimeTick) 210 | } 211 | 212 | // CalcID . 213 | func (m1 *SnowWorkerM1) CalcId(useTimeTick int64) int64 { 214 | result := int64(useTimeTick< -1 { 87 | _client.Del(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(int(value))) 88 | } 89 | } 90 | 91 | _workerIdList = []int32{} 92 | 93 | _workerIdLock.Unlock() 94 | } 95 | 96 | func autoUnRegister() { 97 | // 如果当前已注册过 WorkerId,则先注销,并终止先前的自动续期线程 98 | if len(_workerIdList) > 0 { 99 | //UnRegister() 100 | myUnRegister() 101 | } 102 | } 103 | 104 | func RegisterMany(conf RegisterConf) []int32 { 105 | if conf.MaxWorkerId < 0 || conf.MinWorkerId > conf.MaxWorkerId { 106 | return []int32{-2} 107 | } 108 | 109 | if conf.TotalCount < 1 { 110 | return []int32{-1} 111 | } else if conf.TotalCount == 0 { 112 | conf.TotalCount = 1 113 | } 114 | 115 | _MaxWorkerId = conf.MaxWorkerId 116 | _MinWorkerId = conf.MinWorkerId 117 | _RedisConnString = conf.Address 118 | _RedisPassword = conf.Password 119 | _RedisDB = conf.DB 120 | _RedisMasterName = conf.MasterName 121 | _WorkerIdLifeTimeSeconds = conf.LifeTimeSeconds 122 | 123 | _client = newRedisClient() 124 | if _client == nil { 125 | return []int32{-1} 126 | } 127 | defer func() { 128 | if _client != nil { 129 | _ = _client.Close() 130 | } 131 | }() 132 | 133 | autoUnRegister() 134 | 135 | //_, err := _client.Ping(_ctx).Result() 136 | //if err != nil { 137 | // //panic("init redis error") 138 | // return []int{-3} 139 | //} else { 140 | // if _Log { 141 | // fmt.Println("init redis ok") 142 | // } 143 | //} 144 | 145 | _lifeIndex++ 146 | _workerIdList = make([]int32, conf.TotalCount) 147 | for key := range _workerIdList { 148 | _workerIdList[key] = -1 // 全部初始化-1 149 | } 150 | 151 | useExtendFunc := false 152 | for key := range _workerIdList { 153 | id := register(_lifeIndex) 154 | if id > -1 { 155 | useExtendFunc = true 156 | _workerIdList[key] = id //= append(_workerIdList, id) 157 | } else { 158 | break 159 | } 160 | } 161 | 162 | if useExtendFunc { 163 | go extendLifeTime(_lifeIndex) 164 | } 165 | 166 | return _workerIdList 167 | } 168 | 169 | func RegisterOne(conf RegisterConf) int32 { 170 | if conf.MaxWorkerId < 0 || conf.MinWorkerId > conf.MaxWorkerId { 171 | return -2 172 | } 173 | 174 | _MaxWorkerId = conf.MaxWorkerId 175 | _MinWorkerId = conf.MinWorkerId 176 | _RedisConnString = conf.Address 177 | _RedisPassword = conf.Password 178 | _RedisDB = conf.DB 179 | _RedisMasterName = conf.MasterName 180 | _WorkerIdLifeTimeSeconds = conf.LifeTimeSeconds 181 | _loopCount = 0 182 | 183 | _client = newRedisClient() 184 | if _client == nil { 185 | return -3 186 | } 187 | defer func() { 188 | if _client != nil { 189 | _ = _client.Close() 190 | } 191 | }() 192 | //_, err := _client.Ping(_ctx).Result() 193 | //if err != nil { 194 | // // panic("init redis error") 195 | // return -3 196 | //} else { 197 | // if _Log { 198 | // fmt.Println("init redis ok") 199 | // } 200 | //} 201 | 202 | autoUnRegister() 203 | 204 | _lifeIndex++ 205 | var id = register(_lifeIndex) 206 | if id > -1 { 207 | _workerIdList = []int32{id} 208 | go extendLifeTime(_lifeIndex) 209 | } 210 | 211 | return id 212 | } 213 | 214 | func register(lifeTime int32) int32 { 215 | _loopCount = 0 216 | return getNextWorkerId(lifeTime) 217 | } 218 | 219 | func newRedisClient() redis.UniversalClient { 220 | client := redis.NewUniversalClient(&redis.UniversalOptions{ 221 | Addrs: strings.Split(_RedisConnString, ","), 222 | Password: _RedisPassword, 223 | DB: _RedisDB, 224 | MasterName: _RedisMasterName, 225 | //PoolSize: 1000, 226 | //ReadTimeout: time.Millisecond * time.Duration(100), 227 | //WriteTimeout: time.Millisecond * time.Duration(100), 228 | //IdleTimeout: time.Second * time.Duration(60), 229 | }) 230 | return client 231 | } 232 | 233 | func getNextWorkerId(lifeTime int32) int32 { 234 | // 获取当前 WorkerIdIndex 235 | r, err := _client.Incr(_ctx, _WorkerIdIndexKey).Result() 236 | if err != nil { 237 | return -1 238 | } 239 | 240 | candidateId := int32(r) 241 | 242 | // 设置最小值 243 | if candidateId < _MinWorkerId { 244 | candidateId = _MinWorkerId 245 | setWorkerIdIndex(_MinWorkerId) 246 | } 247 | 248 | if _Log { 249 | fmt.Println("Begin candidateId:" + strconv.Itoa(int(candidateId))) 250 | } 251 | 252 | // 如果 candidateId 大于最大值,则重置 253 | if candidateId > _MaxWorkerId { 254 | if canReset() { 255 | // 当前应用获得重置 WorkerIdIndex 的权限 256 | //setWorkerIdIndex(-1) 257 | setWorkerIdIndex(_MinWorkerId - 1) 258 | endReset() // 此步有可能不被执行? 259 | _loopCount++ 260 | 261 | // 超过一定次数,直接终止操作 262 | if _loopCount > _MaxLoopCount { 263 | _loopCount = 0 264 | 265 | // 返回错误 266 | return -1 267 | } 268 | 269 | // 每次一个大循环后,暂停一些时间 270 | time.Sleep(time.Duration(_SleepMillisecondEveryLoop*_loopCount) * time.Millisecond) 271 | 272 | if _Log { 273 | fmt.Println("canReset loop") 274 | } 275 | 276 | return getNextWorkerId(lifeTime) 277 | } else { 278 | // 如果有其它应用正在编辑,则本应用暂停200ms后,再继续 279 | time.Sleep(time.Duration(200) * time.Millisecond) 280 | 281 | if _Log { 282 | fmt.Println("not canReset loop") 283 | } 284 | 285 | return getNextWorkerId(lifeTime) 286 | } 287 | } 288 | 289 | if _Log { 290 | fmt.Println("candidateId:" + strconv.Itoa(int(candidateId))) 291 | } 292 | 293 | if isAvailable(candidateId) { 294 | if _Log { 295 | fmt.Println("AA: isAvailable:" + strconv.Itoa(int(candidateId))) 296 | } 297 | 298 | // 最新获得的 WorkerIdIndex,在 redis 中是可用状态 299 | setWorkerIdFlag(candidateId) 300 | _loopCount = 0 301 | 302 | // 获取到可用 WorkerId 后,启用新线程,每隔 1/3个 _WorkerIdLifeTimeSeconds 时间,向服务器续期(延长一次 LifeTime) 303 | // go extendWorkerIdLifeTime(lifeTime, candidateId) 304 | 305 | return candidateId 306 | } else { 307 | if _Log { 308 | fmt.Println("BB: not isAvailable:" + strconv.Itoa(int(candidateId))) 309 | } 310 | // 最新获得的 WorkerIdIndex,在 redis 中是不可用状态,则继续下一个 WorkerIdIndex 311 | return getNextWorkerId(lifeTime) 312 | } 313 | } 314 | 315 | func extendLifeTime(lifeIndex int32) { 316 | // 获取到可用 WorkerId 后,启用新线程,每隔 1/3个 _WorkerIdLifeTimeSeconds 时间,向服务器续期(延长一次 LifeTime) 317 | var myLifeIndex = lifeIndex 318 | 319 | // 循环操作:间隔一定时间,刷新 WorkerId 在 redis 中的有效时间。 320 | for { 321 | time.Sleep(time.Duration(_WorkerIdLifeTimeSeconds/3) * time.Second) 322 | 323 | // 上锁操作,防止跟 UnRegister 操作重叠 324 | _workerIdLock.Lock() 325 | 326 | // 如果临时变量 myLifeIndex 不等于 全局变量 _lifeIndex,表明全局状态被修改,当前线程可终止,不应继续操作 redis 327 | // 还应主动释放 redis 键值缓存 328 | if myLifeIndex != _lifeIndex { 329 | break 330 | } 331 | 332 | // 已经被注销,则终止(此步是上一步的二次验证) 333 | if len(_workerIdList) < 1 { 334 | break 335 | } 336 | 337 | // 延长 redis 数据有效期 338 | for _, value := range _workerIdList { 339 | if value > -1 { 340 | extendWorkerIdFlag(value) 341 | } 342 | } 343 | 344 | _workerIdLock.Unlock() 345 | } 346 | } 347 | 348 | func extendWorkerIdLifeTime(lifeIndex int32, workerId int32) { 349 | var myLifeIndex = lifeIndex 350 | var myWorkerId = workerId 351 | 352 | // 循环操作:间隔一定时间,刷新 WorkerId 在 redis 中的有效时间。 353 | for { 354 | time.Sleep(time.Duration(_WorkerIdLifeTimeSeconds/3) * time.Second) 355 | 356 | // 上锁操作,防止跟 UnRegister 操作重叠 357 | _workerIdLock.Lock() 358 | 359 | // 如果临时变量 myLifeIndex 不等于 全局变量 _lifeIndex,表明全局状态被修改,当前线程可终止,不应继续操作 redis 360 | if myLifeIndex != _lifeIndex { 361 | break 362 | } 363 | 364 | // 已经被注销,则终止(此步是上一步的二次验证) 365 | //if _usingWorkerId < 0 { 366 | // break 367 | //} 368 | 369 | // 延长 redis 数据有效期 370 | extendWorkerIdFlag(myWorkerId) 371 | 372 | _workerIdLock.Unlock() 373 | } 374 | } 375 | 376 | func get(key string) (string, bool) { 377 | r, err := _client.Get(_ctx, key).Result() 378 | if err != nil { 379 | return "", false 380 | } 381 | return r, true 382 | } 383 | 384 | func del(key string) (int64, bool) { 385 | r, err := _client.Del(_ctx, key).Result() 386 | if err != nil { 387 | return 0, false 388 | } 389 | return r, true 390 | } 391 | 392 | func set(key string, val string, expTime int32) { 393 | _client.Set(_ctx, key, val, time.Duration(expTime)*time.Second) 394 | } 395 | 396 | func setWorkerIdIndex(val int32) { 397 | _client.Set(_ctx, _WorkerIdIndexKey, val, 0) 398 | } 399 | 400 | func setWorkerIdFlag(workerId int32) { 401 | _client.Set(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(int(workerId)), _WorkerIdFlag, time.Duration(_WorkerIdLifeTimeSeconds)*time.Second) 402 | } 403 | 404 | func extendWorkerIdFlag(workerId int32) { 405 | var client = newRedisClient() 406 | if client == nil { 407 | return 408 | } 409 | defer func() { 410 | if client != nil { 411 | _ = client.Close() 412 | } 413 | }() 414 | 415 | client.Expire(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(int(workerId)), time.Duration(_WorkerIdLifeTimeSeconds)*time.Second) 416 | } 417 | 418 | func canReset() bool { 419 | r, err := _client.Incr(_ctx, _WorkerIdValueKeyPrefix+"Edit").Result() 420 | if err != nil { 421 | return false 422 | } 423 | 424 | if _Log { 425 | fmt.Println("canReset:" + strconv.Itoa(int(r))) 426 | } 427 | 428 | return r != 1 429 | } 430 | 431 | func endReset() { 432 | // _client.Set(_WorkerIdValueKeyPrefix+"Edit", 0, time.Duration(2)*time.Second) 433 | _client.Set(_ctx, _WorkerIdValueKeyPrefix+"Edit", 0, 0) 434 | } 435 | 436 | func getWorkerIdFlag(workerId int32) (string, bool) { 437 | r, err := _client.Get(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(int(workerId))).Result() 438 | if err != nil { 439 | return "", false 440 | } 441 | return r, true 442 | } 443 | 444 | func isAvailable(workerId int32) bool { 445 | r, err := _client.Get(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(int(workerId))).Result() 446 | 447 | if _Log { 448 | fmt.Println("XX isAvailable:" + r) 449 | fmt.Println("YY isAvailable:" + err.Error()) 450 | } 451 | 452 | if err != nil { 453 | if err.Error() == "redis: nil" { 454 | return true 455 | } 456 | return false 457 | } 458 | 459 | return r != _WorkerIdFlag 460 | } 461 | --------------------------------------------------------------------------------