Skip to content

Instantly share code, notes, and snippets.

@byyam
Last active October 24, 2019 15:01
Show Gist options
  • Save byyam/75fd8e1ab6d843c12ea69c80cab0a210 to your computer and use it in GitHub Desktop.
Save byyam/75fd8e1ab6d843c12ea69c80cab0a210 to your computer and use it in GitHub Desktop.
cache in memory
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)
}
}
@byyam
Copy link
Author

byyam commented Oct 24, 2019

源代码解析:
https://github.com/patrickmn/go-cache

key-value存储结构

golang有interface类型可以作为任何value类型的object

type Item struct {
	item         interface{}
	refresh      int64 
}

	items        map[string]Item

缓存锁

使用读写锁,允许同时读

	mu           sync.RWMute

超时清理

使用select和time.NewTicker周期性的清理过期item

func (j *Janitor) Run(c *Cache) {
	ticker := time.NewTicker(j.interval)
	for {
		select {
		case <-ticker.C:
			c.DeleteExpired()
		case <-j.stop:
			ticker.Stop()
			return
		}
	}
}

精简代码demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment