缘起.什么是cache?
cache的中文名叫缓存,缓存在计算机的世界里无处不在,比如cpu的多级缓存,比如类似encache的本地缓存。举个现实中的例子,比如取钱,当然这样的场景被电子支付逐渐取代了,那就把时光倒退到10年前。假设你去饭店吃饭,发现兜里没钱了,去银行取了一顿饭的钱,等到下个饭点,又得去趟银行,不是银行忙死就是你累死,聪明的你,一定不会这么傻,你会取多些出来放在兜里,等下次用直接从兜里拿,你的布袋就是缓存。精明的你一定不会取出太多,取出一万块揣在 兜里,不知道的还以为你肿了呢?缓存不能为缓而缓,要缓的恰到好处。
缘生.为什么用cache?
根据我的经验,主要有两点:
a>提高性能,比如把热点数据放进memcache,redis。
b>减少对后端服务的冲击,给它们减压降温。比如有个服务b,需要被服务a调用。哪一天服务a喝醉昏了头,以超高qps向服务b发起冲击。十有八九,服务b会挂掉。那么,服务a服务b之间加个缓存,问题是不是就化解了。此时,我的耳边响起了那句宛如咒语一样的话:计算机里的问题都可以通过加一层来解决。
缘来.beego是怎么做的?
前面有些跑偏了,言归正传。
beego支持多个cache类型,内存、redis、file、memcache、ssdb。先看一下接口定义:
type Cache interface {
// get cached value by key.
Get(key string)interface{}
// GetMulti is a batch version of Get.
GetMulti(keys []string) []interface{}
// set cached value with key and expire time.
Put(key string, valinterface{}, timeout time.Duration) error
// delete cached value by key.
Delete(key string) error
// increase cached int value by key, as a counter.
Incr(key string) error
// decrease cached int value by key, as a counter.
Decr(key string) error
// check if cached value exists or not.
IsExist(key string) bool
// clear all cache.
ClearAll() error
// start gc routine based on config string settings.
StartAndGC(config string) error
}
望文生义,其他函数功能不多解释。主要说说StartAndGC。这个函数主要用来初始连接与清除缓存,redis与memcache可以设置过期时间的缓存适配器是不需要做清除操作的,完全交由他们自己来做。内存缓存、文件缓存就需要自己费神了。简单看一下MemoryCache的实现。
// StartAndGC start memory cache. it will check expiration in every clock time.
func (bc *MemoryCache) StartAndGC(config string) error {
var cfmap[string]int
json.Unmarshal([]byte(config), &cf)
if _, ok := cf["interval"]; !ok {
cf = make(map[string]int)
cf["interval"] = DefaultEvery
}
dur := time.Duration(cf["interval"]) * time.Second
bc.Every = cf["interval"]
bc.dur = dur
go bc.vacuum()
return nil
}
主要看go bc.vacuum()
这里起了个goroutine,来做过期检测,并清除过期值。
// check expiration.
func (bc *MemoryCache) vacuum() {
bc.RLock()
every := bc.Every
bc.RUnlock()
if every <1 {
return
}
for {
<-time.After(bc.dur)
if bc.items == nil {
return
}
if keys := bc.expiredKeys(); len(keys) !=0 {
bc.clearItems(keys)
}
}
}
清除其实就是从map里面删除掉:
// clearItems removes all the items which key in keys.
func (bc *MemoryCache) clearItems(keys []string) {
bc.Lock()
defer bc.Unlock()
for _, key :=range keys {
delete(bc.items, key)
}
}
其他缓存适配器大同小异,当然,你也可以自己实现一个cache,只要实现上面那个cache接口 即可。
有了这些适配器,怎么找到并使用他们呢?
// Instance is a function create a new Cache Instance
type Instancefunc() Cache
var adapters = make(map[string]Instance)
// Register makes a cache adapter available by the adapter name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, adapterInstance) {
if adapter == nil {
panic("cache: Register adapter is nil")
}
if _, ok := adapters[name]; ok {
panic("cache: Register called twice for adapter " + name)
}
adapters[name] = adapter
}
每个适配器文件里实现了init方法,
func init() {
Register("memory", NewMemoryCache)
}
原来是把适配器注册到了全局变量里。如果要使用适配器,只需传入适配器类型便可。服务很是到位:
func test(){
bm, err := cache.NewCache("redis",`{"conn": "127.0.0.1:6379"}`)
if err != nil {
t.Error("init err")
}
timeoutDuration :=10 * time.Second
if err = bm.Put("astaxie",1, timeoutDuration); err != nil {
t.Error("set Error", err)
}
}
缘终:技术没有终点
第一次写技术博客,很粗糙。看过不如写过,写过不如用过。