You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

reghelper.go 6.7 kB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package regworkerid
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/go-redis/redis/v8"
  6. "strconv"
  7. "sync"
  8. "time"
  9. )
  10. var _client *redis.Client
  11. var _ctx = context.Background()
  12. var _workerIdLock sync.Mutex
  13. var _usingWorkerId int = -1 // 当前已注册的WorkerId
  14. var _loopCount int = 0 // 循环数量
  15. var _lifeIndex int = -1 // WorkerId本地生命时序(本地多次注册时,生命时序会不同)
  16. var _token int = -1 // WorkerId远程注册时用的token,将存储在 IdGen:WorkerId:Value:xx 的值中(本功能暂未启用)
  17. var _WorkerIdLifeTimeSeconds = 15 // IdGen:WorkerId:Value:xx 的值在 redis 中的有效期(单位秒,最好是3的整数倍)
  18. var _MaxLoopCount = 10 // 最大循环次数(无可用WorkerId时循环查找)
  19. var _SleepMillisecondEveryLoop = 200 // 每次循环后,暂停时间
  20. var _MaxWorkerId int = 0 // 最大WorkerId值,超过此值从0开始
  21. const _WorkerIdIndexKey string = "IdGen:WorkerId:Index" // redis 中的key
  22. const _WorkerIdValueKeyPrefix string = "IdGen:WorkerId:Value:" // redis 中的key
  23. const _WorkerIdFlag = "Y" // IdGen:WorkerId:Value:xx 的值(将来可用 _token 替代)
  24. const _Log = false // 是否输出日志
  25. func ValidateLocalWorkerId(workerId int) int {
  26. if workerId == _usingWorkerId {
  27. return 0
  28. } else {
  29. return -1
  30. }
  31. }
  32. func UnRegisterWorkerId() {
  33. if _usingWorkerId < 0 {
  34. return
  35. }
  36. _workerIdLock.Lock()
  37. _client.Del(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(_usingWorkerId))
  38. _usingWorkerId = -1
  39. _lifeIndex = -1
  40. _workerIdLock.Unlock()
  41. _client.Quit(_ctx)
  42. }
  43. func RegisterWorkerId(ip string, port int, password string, maxWorkerId int) int {
  44. // maxWorkerId不能小于0
  45. if maxWorkerId < 0 {
  46. return -1
  47. }
  48. // 如果当前已注册过 WorkerId,则先注销,并终止先前的自动续期线程
  49. if _usingWorkerId > -1 {
  50. UnRegisterWorkerId()
  51. }
  52. _MaxWorkerId = maxWorkerId
  53. _client = redis.NewClient(&redis.Options{
  54. Addr: ip + ":" + strconv.Itoa(port),
  55. Password: password,
  56. DB: 0,
  57. //PoolSize: 1000,
  58. //ReadTimeout: time.Millisecond * time.Duration(100),
  59. //WriteTimeout: time.Millisecond * time.Duration(100),
  60. //IdleTimeout: time.Second * time.Duration(60),
  61. })
  62. _, err := _client.Ping(_ctx).Result()
  63. if err != nil {
  64. panic("init redis error")
  65. } else {
  66. if _Log {
  67. fmt.Println("init redis ok")
  68. }
  69. }
  70. _loopCount = 0
  71. return getNextWorkerId()
  72. }
  73. func getNextWorkerId() int {
  74. // 获取当前 WorkerIdIndex
  75. r, err := _client.Incr(_ctx, _WorkerIdIndexKey).Result()
  76. if err != nil {
  77. return 0
  78. }
  79. candidateId := int(r)
  80. if _Log {
  81. fmt.Println("Begin candidateId:" + strconv.Itoa(candidateId))
  82. }
  83. // 如果 candidateId 大于最大值,则重置
  84. if candidateId > _MaxWorkerId {
  85. if canReset() {
  86. // 当前应用获得重置 WorkerIdIndex 的权限
  87. setWorkerIdIndex(-1)
  88. endReset() // 此步有可能不被执行?
  89. _loopCount++
  90. // 超过一定次数,直接终止操作
  91. if _loopCount > _MaxLoopCount {
  92. _loopCount = 0
  93. return -1
  94. }
  95. // 每次一个大循环后,暂停一些时间
  96. time.Sleep(time.Duration(_SleepMillisecondEveryLoop*_loopCount) * time.Millisecond)
  97. if _Log {
  98. fmt.Println("canReset loop")
  99. }
  100. return getNextWorkerId()
  101. } else {
  102. // 如果有其它应用正在编辑,则本应用暂停200ms后,再继续
  103. time.Sleep(time.Duration(200) * time.Millisecond)
  104. if _Log {
  105. fmt.Println("not canReset loop")
  106. }
  107. return getNextWorkerId()
  108. }
  109. }
  110. if _Log {
  111. fmt.Println("candidateId:" + strconv.Itoa(candidateId))
  112. }
  113. if isAvailable(candidateId) {
  114. if _Log {
  115. fmt.Println("AA: isAvailable:" + strconv.Itoa(candidateId))
  116. }
  117. // 最新获得的 WorkerIdIndex,在 redis 中是可用状态
  118. setWorkerIdFlag(candidateId)
  119. _usingWorkerId = candidateId
  120. _loopCount = 0
  121. // 获取到可用 WorkerId 后,启用新线程,每隔 1/3个 _WorkerIdLifeTimeSeconds 时间,向服务器续期(延长一次 LifeTime)
  122. _lifeIndex++
  123. go extendWorkerIdLifeTime(_lifeIndex)
  124. return candidateId
  125. } else {
  126. if _Log {
  127. fmt.Println("BB: not isAvailable:" + strconv.Itoa(candidateId))
  128. }
  129. // 最新获得的 WorkerIdIndex,在 redis 中是不可用状态,则继续下一个 WorkerIdIndex
  130. return getNextWorkerId()
  131. }
  132. }
  133. func extendWorkerIdLifeTime(lifeIndex int) {
  134. var myLifeIndex = lifeIndex
  135. // 循环操作:间隔一定时间,刷新 WorkerId 在 redis 中的有效时间。
  136. for {
  137. time.Sleep(time.Duration(_WorkerIdLifeTimeSeconds/3) * time.Millisecond)
  138. // 上锁操作,防止跟 UnRegisterWorkerId 操作重叠
  139. _workerIdLock.Lock()
  140. // 如果临时变量 myLifeIndex 不等于 全局变量 _lifeIndex,表明全局状态被修改,当前线程可终止,不应继续操作 redis
  141. if myLifeIndex != _lifeIndex {
  142. break
  143. }
  144. // 已经被注销,则终止(此步是上一步的二次验证)
  145. if _usingWorkerId < 0 {
  146. break
  147. }
  148. // 延长 redis 数据有效期
  149. extendWorkerIdFlag(_usingWorkerId)
  150. _workerIdLock.Unlock()
  151. }
  152. }
  153. func get(key string) (string, bool) {
  154. r, err := _client.Get(_ctx, key).Result()
  155. if err != nil {
  156. return "", false
  157. }
  158. return r, true
  159. }
  160. func set(key string, val string, expTime int32) {
  161. _client.Set(_ctx, key, val, time.Duration(expTime)*time.Second)
  162. }
  163. func setWorkerIdIndex(val int) {
  164. _client.Set(_ctx, _WorkerIdIndexKey, val, 0)
  165. }
  166. func setWorkerIdFlag(index int) {
  167. _client.Set(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(index), _WorkerIdFlag, time.Duration(_WorkerIdLifeTimeSeconds)*time.Second)
  168. }
  169. func extendWorkerIdFlag(index int) {
  170. _client.Expire(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(index), time.Duration(_WorkerIdLifeTimeSeconds)*time.Second)
  171. }
  172. func canReset() bool {
  173. r, err := _client.Incr(_ctx, _WorkerIdValueKeyPrefix+"Edit").Result()
  174. if err != nil {
  175. return false
  176. }
  177. if _Log {
  178. fmt.Println("canReset:" + string(r))
  179. }
  180. return r != 1
  181. }
  182. func endReset() {
  183. // _client.Set(_WorkerIdValueKeyPrefix+"Edit", 0, time.Duration(2)*time.Second)
  184. _client.Set(_ctx, _WorkerIdValueKeyPrefix+"Edit", 0, 0)
  185. }
  186. func getWorkerIdFlag(index int) (string, bool) {
  187. r, err := _client.Get(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(index)).Result()
  188. if err != nil {
  189. return "", false
  190. }
  191. return r, true
  192. }
  193. func isAvailable(index int) bool {
  194. r, err := _client.Get(_ctx, _WorkerIdValueKeyPrefix+strconv.Itoa(index)).Result()
  195. if _Log {
  196. fmt.Println("XX isAvailable:" + r)
  197. fmt.Println("YY isAvailable:" + err.Error())
  198. }
  199. if err != nil {
  200. if err.Error() == "redis: nil" {
  201. return true
  202. }
  203. return false
  204. }
  205. return r != _WorkerIdFlag
  206. }

雪花算法中非常好用的数字ID生成器