Last active
October 24, 2019 15:01
-
-
Save byyam/75fd8e1ab6d843c12ea69c80cab0a210 to your computer and use it in GitHub Desktop.
cache in memory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"sync" | |
"time" | |
"runtime" | |
) | |
type keyAndValue struct { | |
key string | |
value interface{} | |
} | |
type Item struct { | |
item interface{} | |
refresh int64 | |
} | |
type Janitor struct { | |
interval time.Duration | |
stop chan bool | |
} | |
type Cache struct { | |
expiry time.Duration | |
items map[string]Item | |
mu sync.RWMutex | |
onEvicted func(string, interface{}) | |
janitor *Janitor | |
} | |
func (c *Cache) delete(k string) (interface{}, bool) { | |
if c.onEvicted != nil { | |
if v, found := c.items[k]; found { | |
delete(c.items, k) | |
return v.item, true | |
} | |
} | |
fmt.Printf("delete, key=%s, items:%+v\n", k, c.items) | |
delete(c.items, k) | |
return nil, false | |
} | |
func (c *Cache) DeleteExpired() { | |
var evictedItems []keyAndValue | |
now := time.Now().UnixNano() | |
c.mu.Lock() | |
for k, v := range c.items { | |
fmt.Printf("delete, now=%d, refresh:%d\n", now, v.refresh) | |
if v.refresh > 0 && now > v.refresh { | |
ov, evicted := c.delete(k) | |
if evicted { | |
evictedItems = append(evictedItems, keyAndValue{k, ov}) | |
} | |
} | |
} | |
c.mu.Unlock() | |
for _, v := range evictedItems { | |
c.onEvicted(v.key, v.value) | |
} | |
} | |
func (j *Janitor) Run(c *Cache) { | |
ticker := time.NewTicker(j.interval) | |
for { | |
select { | |
case <-ticker.C: | |
c.DeleteExpired() | |
case <-j.stop: | |
ticker.Stop() | |
return | |
} | |
} | |
} | |
func stopJanitor(c *Cache) { | |
c.janitor.stop <- true | |
} | |
func runJanitor(c *Cache, ci time.Duration) { | |
j := &Janitor{ | |
interval: ci, | |
stop: make(chan bool), | |
} | |
c.janitor = j | |
go j.Run(c) | |
} | |
func New(expiry, cleanup time.Duration) *Cache { | |
items := make(map[string]Item) | |
c := &Cache{ | |
expiry : expiry, | |
items : items, | |
} | |
if cleanup > 0 { | |
runJanitor(c, cleanup) | |
runtime.SetFinalizer(c, stopJanitor) | |
} | |
return c | |
} | |
func (c *Cache) Set(k string, x interface{}) { | |
e := time.Now().Add(c.expiry).UnixNano() | |
c.mu.Lock() | |
c.items[k] = Item{ | |
item: x, | |
refresh: e, | |
} | |
fmt.Printf("set:%+v\n", c.items) | |
c.mu.Unlock() | |
} | |
func (c *Cache) Get(k string) (interface{}, bool) { | |
c.mu.RLock() | |
fmt.Printf("get:%+v\n", c.items) | |
item, found := c.items[k] | |
if !found { | |
c.mu.RUnlock() | |
return nil, false | |
} | |
if item.refresh > 0 { | |
if time.Now().UnixNano() > item.refresh { | |
c.mu.RUnlock() | |
return nil, false | |
} | |
} | |
c.mu.RUnlock() | |
return item.item, true | |
} | |
func main() { | |
obj := New(time.Second * 5, time.Second * 1) | |
fmt.Printf("obj:%+v\n", obj) | |
obj.Set("a", "abc") | |
time.Sleep(3*time.Second) | |
if value, ok := obj.Get("a"); ok { | |
fmt.Printf("get value: %+v\n", value) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
源代码解析:
https://github.com/patrickmn/go-cache
key-value存储结构
golang有interface类型可以作为任何value类型的object
缓存锁
使用读写锁,允许同时读
超时清理
使用select和time.NewTicker周期性的清理过期item
精简代码demo