gitee.com/zhongguo168a/gocodes@v0.0.0-20230609140523-e1828349603f/myx/cachex/expired.go (about) 1 package cachex 2 3 import ( 4 "time" 5 ) 6 7 type expiredItem struct { 8 key string 9 handler func(key string, data interface{}) 10 time time.Time 11 12 next *expiredItem 13 prev *expiredItem 14 } 15 16 func (item *expiredItem) remove() { 17 item_prev := item.prev 18 if item_prev != nil { 19 item_prev.next = item.next 20 } 21 22 item_next := item.next 23 if item_next != nil { 24 item_next.prev = item.prev 25 } 26 27 item.next = nil 28 item.prev = nil 29 30 } 31 32 func (item *expiredItem) insertAfter(new *expiredItem) { 33 item_next := item.next 34 item.next, new.prev = new, item 35 if item_next != nil { 36 item.next.prev = new 37 new.next = item_next 38 } 39 } 40 41 func (item *expiredItem) insertBefore(new *expiredItem) { 42 item_prev := item.prev 43 item.prev, new.next = new, item 44 if item_prev != nil { 45 item_prev.next = new 46 new.prev = item_prev 47 } 48 } 49 50 func (c *Cache) SetExpiredSpace(t time.Duration) { 51 c.expiredSpace = t 52 } 53 54 // 由于采用了标记的方式清楚expire, 所以可能会出现一种情况: 55 // 清理后, list中残留了一个键为[key]的expiredItem, 但后面只再次Set了[key]的值, 但没有Expire, 会导致expiredItem仍然生效 56 // 因此, 使用的时候需要注意这点 57 func (c *Cache) Expire(key string, duration time.Duration, handler func(key string, data interface{})) { 58 var ( 59 start *expiredItem // 起始节点 60 right bool // 是否往右, 即时间递增的方向 61 ) 62 newTime := time.Now().Add(duration) 63 newItem, existed := c.expiredSet[key] 64 if existed { 65 newItem.handler = handler 66 if newTime.Equal(newItem.time) { 67 return 68 } 69 if newTime.After(newItem.time) { 70 start = newItem.next 71 right = true 72 } else { 73 start = newItem.prev 74 } 75 newItem.time = newTime 76 if start == nil { 77 // 不需要处理 78 return 79 } 80 81 if newItem == c.expiredRoot { 82 c.expiredRoot = newItem.next 83 } 84 newItem.remove() 85 86 } else { 87 newItem = &expiredItem{ 88 key: key, 89 handler: handler, 90 time: newTime, 91 } 92 93 c.mu.Lock() 94 c.expiredSet[key] = newItem 95 c.mu.Unlock() 96 // 第一个 97 if c.expiredRoot == nil { 98 c.expiredRoot = newItem 99 return 100 } 101 102 start = c.expiredRoot 103 right = true 104 } 105 106 if right { 107 cur := start 108 for { 109 if newItem.time.Before(cur.time) { 110 cur.insertBefore(newItem) 111 if c.expiredRoot == cur { 112 c.expiredRoot = newItem 113 } 114 break 115 } 116 117 if cur.next == nil { 118 cur.insertAfter(newItem) 119 break 120 } 121 122 cur = cur.next 123 } 124 } else { 125 cur := start 126 for { 127 if newItem.time.After(cur.time) { 128 cur.insertAfter(newItem) 129 break 130 } 131 132 if cur.prev == nil { 133 cur.insertBefore(newItem) 134 if c.expiredRoot == cur { 135 c.expiredRoot = newItem 136 } 137 break 138 } 139 140 cur = cur.prev 141 } 142 } 143 } 144 145 func (c *Cache) cleanExpireKey(key string) { 146 147 } 148 149 func (c *Cache) cleanExpireItem(item *expiredItem) { 150 data, has := c.Get(item.key) 151 if has && item.handler != nil { 152 item.handler(item.key, data) 153 } 154 item.remove() 155 c.mu.Lock() 156 delete(c.expiredSet, item.key) 157 c.mu.Unlock() 158 c.Delete(item.key) 159 } 160 161 func (c *Cache) goCleanExpireList() { 162 go func() { 163 for { 164 c.cleanExpireList() 165 time.Sleep(c.expiredSpace) 166 } 167 }() 168 } 169 170 func (c *Cache) cleanExpireList() { 171 if c.expiredRoot == nil { 172 return 173 } 174 175 now := time.Now() 176 cur := c.expiredRoot 177 for { 178 if now.Before(cur.time) { 179 break 180 } 181 // 182 c.expiredRoot = cur.next 183 c.cleanExpireItem(cur) 184 185 if cur.next == nil { 186 break 187 } 188 189 cur = cur.next 190 } 191 }