有些时候,应用系统用不上 redis,我们也可以用锁和 goroutine 实现一个带有过期时间的线程安全的字典。
这种字典的应用场景,比较倾向于数据规模较小,没有分布式要求。
下面是实现:
1、定义结构
type Item struct {
value interface{}
expireAt int64
}
type TTLMap struct {
m map[string]*Item
mu sync.Mutex
}
字典的值是一个可以接收任何类型的 interface{}
。
2、定义行为
func NewTTLMap(size int) (m *TTLMap) {
m = &TTLMap{m: make(map[string]*Item, size)}
go func() {
for now := range time.Tick(time.Second) {
m.mu.Lock()
for k, v := range m.m {
if v.expireAt <= now.Unix() {
delete(m.m, k)
}
}
m.mu.Unlock()
}
}()
return
}
func (m *TTLMap) Set(key string, value interface{}, ttl int64) {
m.mu.Lock()
defer m.mu.Unlock()
m.m[key] = &Item{value: value, expireAt: time.Now().Unix() + ttl}
}
func (m *TTLMap) Get(key string) (v interface{}, ok bool) {
m.mu.Lock()
defer m.mu.Unlock()
if item, ok := m.m[key]; ok && item.expireAt > time.Now().Unix() {
return item.value, true
}
return nil, false
}
NewTTLMap
函数用来初始化字典,然后使用 goroutine 开启新的轻量级线程,按照一定的频率从字典里删除项。
Get
函数用来获取字典的值,然后这里也判断一下过期时间,如果已经过期了,就不再返回了。
3、验证结果
func TestTTLMap(t *testing.T) {
m := NewTTLMap(10)
m.Set("hello", "world", 5)
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
if v, ok := m.Get("hello"); ok {
t.Log(v)
} else {
t.Log("expired")
}
}
}
输出结果:
=== RUN TestTTLMap
ttl_map_test.go:64: world
ttl_map_test.go:64: world
ttl_map_test.go:64: world
ttl_map_test.go:64: world
ttl_map_test.go:66: expired
ttl_map_test.go:66: expired
ttl_map_test.go:66: expired
ttl_map_test.go:66: expired
ttl_map_test.go:66: expired
ttl_map_test.go:66: expired
--- PASS: TestTTLMap (10.00s)
PASS
每秒读取一次,由于过期时间是 5 秒,因此,5 秒之后就读取不到值了。
Over!
标签:map,映射,过期,go,test,ttl,Go,expired,字典 From: https://www.cnblogs.com/denglei1024/p/18499819