Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
176 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package thqcache | ||
|
||
//ByteValue 数据格式封装 | ||
type ByteValue struct { | ||
b []byte | ||
} | ||
|
||
func (v ByteValue) Len() int { | ||
return len(v.b) | ||
} | ||
|
||
func (v ByteValue) ByteSlice() []byte { | ||
return cloneBytes(v.b) | ||
} | ||
|
||
func (v ByteValue) String() string { | ||
return string(v.b) | ||
} | ||
|
||
//cloneBytes 深拷贝防止修改b | ||
func cloneBytes(b []byte) []byte { | ||
c := make([]byte, len(b)) | ||
copy(c, b) | ||
return c | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package thqcache | ||
|
||
import ( | ||
"github.com/ithaiq/thqcache/lru" | ||
"sync" | ||
) | ||
|
||
//cache lru Cache封装 + 锁并发控制 | ||
type cache struct { | ||
lru *lru.Cache | ||
mu sync.Mutex | ||
maxBytes int64 | ||
} | ||
|
||
func (this *cache) add(key string, value ByteValue) { | ||
this.mu.Lock() | ||
defer this.mu.Unlock() | ||
//懒加载 性能考虑 | ||
if this.lru == nil { | ||
this.lru = lru.NewCache(this.maxBytes, nil) | ||
} | ||
this.lru.Add(key, value) | ||
} | ||
|
||
func (this *cache) get(key string) (value ByteValue, ok bool) { | ||
this.mu.Lock() | ||
defer this.mu.Unlock() | ||
if this.lru == nil { | ||
return | ||
} | ||
if v, ok := this.lru.Get(key); ok { | ||
return v.(ByteValue), true | ||
} | ||
return | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package thqcache | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
) | ||
|
||
type Getter interface { | ||
Get(key string) ([]byte, error) | ||
} | ||
|
||
//接口型函数 | ||
//方便使用者在调用时既能够传入函数作为参数,也能够传入实现了该接口的结构体作为参数。 | ||
|
||
type GetterFunc func(key string) ([]byte, error) | ||
|
||
func (f GetterFunc) Get(key string) ([]byte, error) { | ||
return f(key) | ||
} | ||
|
||
type Group struct { | ||
name string | ||
getter Getter | ||
c cache | ||
} | ||
|
||
var ( | ||
mu sync.RWMutex | ||
groups = make(map[string]*Group) | ||
) | ||
|
||
func NewGroup(name string, maxBytes int64, getter Getter) *Group { | ||
if getter == nil { | ||
panic("nil Getter") | ||
} | ||
mu.Lock() | ||
defer mu.Unlock() | ||
g := &Group{ | ||
name: name, | ||
getter: getter, | ||
c: cache{maxBytes: maxBytes}, | ||
} | ||
groups[name] = g | ||
return g | ||
} | ||
|
||
func GetGroup(name string) *Group { | ||
mu.RLock() | ||
defer mu.RUnlock() | ||
return groups[name] | ||
} | ||
|
||
func (this *Group) Add(key string, value ByteValue) { | ||
this.c.add(key, value) | ||
} | ||
|
||
func (this *Group) Get(key string) (ByteValue, error) { | ||
if key == "" { | ||
return ByteValue{}, fmt.Errorf("key is empty") | ||
} | ||
if v, ok := this.c.get(key); ok { | ||
return v, nil | ||
} | ||
return this.loadLocal(key) | ||
} | ||
|
||
//loadLocal 加载本地数据到缓存 | ||
func (this *Group) loadLocal(key string) (ByteValue, error) { | ||
bytes, err := this.getter.Get(key) | ||
if err != nil { | ||
return ByteValue{}, err | ||
} | ||
value := ByteValue{b: cloneBytes(bytes)} | ||
this.Add(key, value) | ||
return value, nil | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package thqcache | ||
|
||
import ( | ||
"fmt" | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
var db = map[string]string{ | ||
"thq1": "1", | ||
"thq2": "2", | ||
"thq3": "3", | ||
} | ||
|
||
func TestGroup_Get(t *testing.T) { | ||
loadCount := make(map[string]int, len(db)) | ||
getter := func(key string) ([]byte, error) { | ||
if v, ok := db[key]; ok { | ||
if _, ok := loadCount[key]; !ok { | ||
loadCount[key] = 0 | ||
} | ||
loadCount[key]++ | ||
return []byte(v), nil | ||
} | ||
return nil, fmt.Errorf("%s not exist", key) | ||
} | ||
|
||
g := NewGroup("test", 2<<10, GetterFunc(getter)) | ||
|
||
for k, v := range db { | ||
value, err := g.Get(k) | ||
require.NoError(t, err) | ||
require.Equal(t, value.String(), v) | ||
_, err = g.Get(k) | ||
require.NoError(t, err) | ||
require.True(t, loadCount[k] == 1) | ||
} | ||
_, err := g.Get("no") | ||
require.True(t, err != nil) | ||
} |