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

116 lines
2.3 KiB

  1. package uid
  2. import (
  3. "database/sql"
  4. "errors"
  5. "log"
  6. "time"
  7. )
  8. type logger interface {
  9. Error(error)
  10. }
  11. // Logger Log接口,如果设置了Logger,就使用Logger打印日志,如果没有设置,就使用内置库log打印日志
  12. var Logger logger
  13. // ErrTimeOut 获取uid超时错误
  14. var ErrTimeOut = errors.New("get uid timeout")
  15. type Uid struct {
  16. db *sql.DB // 数据库连接
  17. businessId string // 业务id
  18. ch chan int64 // id缓冲池
  19. min, max int64 // id段最小值,最大值
  20. }
  21. // NewUid 创建一个Uid;len:缓冲池大小()
  22. // db:数据库连接
  23. // businessId:业务id
  24. // len:缓冲池大小(长度可控制缓存中剩下多少id时,去DB中加载)
  25. func NewUid(db *sql.DB, businessId string, len int) (*Uid, error) {
  26. lid := Uid{
  27. db: db,
  28. businessId: businessId,
  29. ch: make(chan int64, len),
  30. }
  31. go lid.productId()
  32. return &lid, nil
  33. }
  34. // Get 获取自增id,当发生超时,返回错误,避免大量请求阻塞,服务器崩溃
  35. func (u *Uid) Get() (int64, error) {
  36. select {
  37. case <-time.After(1 * time.Second):
  38. return 0, ErrTimeOut
  39. case uid := <-u.ch:
  40. return uid, nil
  41. }
  42. }
  43. // productId 生产id,当ch达到最大容量时,这个方法会阻塞,直到ch中的id被消费
  44. func (u *Uid) productId() {
  45. _ = u.reLoad()
  46. for {
  47. if u.min >= u.max {
  48. _ = u.reLoad()
  49. }
  50. u.min++
  51. u.ch <- u.min
  52. }
  53. }
  54. // reLoad 在数据库获取id段,如果失败,会每隔一秒尝试一次
  55. func (u *Uid) reLoad() error {
  56. var err error
  57. for {
  58. err = u.getFromDB()
  59. if err == nil {
  60. return nil
  61. }
  62. // 数据库发生异常,等待一秒之后再次进行尝试
  63. if Logger != nil {
  64. Logger.Error(err)
  65. } else {
  66. log.Println(err)
  67. }
  68. time.Sleep(time.Second)
  69. }
  70. }
  71. // getFromDB 从数据库获取id段
  72. func (u *Uid) getFromDB() error {
  73. var (
  74. maxId int64
  75. step int64
  76. )
  77. tx, err := u.db.Begin()
  78. if err != nil {
  79. return err
  80. }
  81. defer func() { _ = tx.Rollback() }()
  82. row := tx.QueryRow("SELECT max_id,step FROM uid WHERE business_id = ? FOR UPDATE", u.businessId)
  83. err = row.Scan(&maxId, &step)
  84. if err != nil {
  85. return err
  86. }
  87. _, err = tx.Exec("UPDATE uid SET max_id = ? WHERE business_id = ?", maxId+step, u.businessId)
  88. if err != nil {
  89. return err
  90. }
  91. err = tx.Commit()
  92. if err != nil {
  93. return err
  94. }
  95. u.min = maxId
  96. u.max = maxId + step
  97. return nil
  98. }