mem.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package reqlimit
  2. import (
  3. "strings"
  4. "sync"
  5. "sync/atomic"
  6. "time"
  7. )
  8. type windowCounts struct {
  9. normal int64
  10. over int64
  11. }
  12. type entry struct {
  13. sync.Mutex
  14. windows map[int64]*windowCounts
  15. lastAccess atomic.Value
  16. }
  17. type InMemoryRecord struct {
  18. entries sync.Map
  19. }
  20. func NewInMemoryRecord() *InMemoryRecord {
  21. rl := &InMemoryRecord{
  22. entries: sync.Map{},
  23. }
  24. go rl.cleanupInactiveEntries(2*time.Minute, 1*time.Minute)
  25. return rl
  26. }
  27. func (m *InMemoryRecord) getEntry(keys []string) *entry {
  28. key := strings.Join(keys, ":")
  29. actual, _ := m.entries.LoadOrStore(key, &entry{
  30. windows: make(map[int64]*windowCounts),
  31. })
  32. e, _ := actual.(*entry)
  33. if e.lastAccess.Load() == nil {
  34. e.lastAccess.CompareAndSwap(nil, time.Now())
  35. }
  36. return e
  37. }
  38. func (m *InMemoryRecord) cleanupAndCount(e *entry, cutoff int64) (int64, int64) {
  39. normalCount := int64(0)
  40. overCount := int64(0)
  41. for ts, wc := range e.windows {
  42. if ts < cutoff {
  43. delete(e.windows, ts)
  44. } else {
  45. normalCount += wc.normal
  46. overCount += wc.over
  47. }
  48. }
  49. return normalCount, overCount
  50. }
  51. func (m *InMemoryRecord) PushRequest(
  52. overed int64,
  53. duration time.Duration,
  54. n int64,
  55. keys ...string,
  56. ) (normalCount, overCount, secondCount int64) {
  57. e := m.getEntry(keys)
  58. e.Lock()
  59. defer e.Unlock()
  60. now := time.Now()
  61. e.lastAccess.Store(now)
  62. windowStart := now.Unix()
  63. cutoff := windowStart - int64(duration.Seconds())
  64. normalCount, overCount = m.cleanupAndCount(e, cutoff)
  65. wc, exists := e.windows[windowStart]
  66. if !exists {
  67. wc = &windowCounts{}
  68. e.windows[windowStart] = wc
  69. }
  70. if overed == 0 || normalCount <= overed {
  71. wc.normal += n
  72. normalCount += n
  73. } else {
  74. wc.over += n
  75. overCount += n
  76. }
  77. return normalCount, overCount, wc.normal + wc.over
  78. }
  79. func (m *InMemoryRecord) GetRequest(
  80. duration time.Duration,
  81. keys ...string,
  82. ) (totalCount, secondCount int64) {
  83. nowSecond := time.Now().Unix()
  84. cutoff := nowSecond - int64(duration.Seconds())
  85. m.entries.Range(func(key, value any) bool {
  86. k, _ := key.(string)
  87. currentKeys := parseKeys(k)
  88. if matchKeys(keys, currentKeys) {
  89. e, _ := value.(*entry)
  90. e.Lock()
  91. normalCount, overCount := m.cleanupAndCount(e, cutoff)
  92. nowWindow := e.windows[nowSecond]
  93. e.Unlock()
  94. totalCount += normalCount + overCount
  95. if nowWindow != nil {
  96. secondCount += nowWindow.normal + nowWindow.over
  97. }
  98. }
  99. return true
  100. })
  101. return totalCount, secondCount
  102. }
  103. func (m *InMemoryRecord) cleanupInactiveEntries(interval, maxInactivity time.Duration) {
  104. ticker := time.NewTicker(interval)
  105. defer ticker.Stop()
  106. for range ticker.C {
  107. m.entries.Range(func(key, value any) bool {
  108. e, _ := value.(*entry)
  109. la := e.lastAccess.Load()
  110. if la == nil {
  111. return true
  112. }
  113. lastAccess, _ := la.(time.Time)
  114. if time.Since(lastAccess) > maxInactivity {
  115. m.entries.CompareAndDelete(key, e)
  116. }
  117. return true
  118. })
  119. }
  120. }
  121. func parseKeys(key string) []string {
  122. return strings.Split(key, ":")
  123. }
  124. func matchKeys(pattern, keys []string) bool {
  125. if len(pattern) != len(keys) {
  126. return false
  127. }
  128. for i, p := range pattern {
  129. if p != "*" && p != keys[i] {
  130. return false
  131. }
  132. }
  133. return true
  134. }