为满足特定的业务需求,我们有些接口数据极大,返回给客户端的数据 gzip 后大概是 80KB 左右, 不做 gzip 的话大概有 600KB 的样子。
由于 golang 与 php 的实现是分别由两个团队完成的,考虑到实现的可行性,我们先考虑通过 nginx 的 fastcgi_cache 来实现静态接口的缓存。这类接口并不要求实时更新,且大多通过 php 实现,因此完全可以通过 nginx fastcgi_cache 实现。而且此方案还有一些额外的好处,譬如避免请求蜂拥至后端,若后端出错可直接返回老数据等等。
然而,对于动态接口而言,这个方案完全没有办法满足需求。幸而动态接口基本都是基于 golang 实现的,因此我们只需要引入一种本地内存缓存方案,就可以很好的解决前面提及的三个问题。
经过一周左右时间的调研,我们考察了 golang-lru 、 go-cache 、 groupcache 、 freecache 及 bigcache 等各种缓存库,出于简单稳定高效的角度,我们最终选用了 golang-lru ,并在此基础上实现了 expire feature ,参考链接:GitHub - hnlq715/golang-lru: Golang LRU cache with expire feature.
与此同时,我们也参考了 groupcache 的特性,实现了类似逻辑,避免同时涌入过多请求到 redis :
func (l *lruCache) GetWithLoader(ctx context.Context, key string, load GetterFunc) (interface{}, error) {
l.stats.Gets.Add(1)
data, ok := l.arc.Get(key)
if !ok && load != nil {
return l.g.Do(key, func() (interface{}, error) {
l.stats.Loads.Add(1)
data, err := load()
if err != nil {
return nil, err
}
l.Set(ctx, key, data)
return data, err
})
}
l.stats.Hits.Add(1)
return data, nil
}
通过这个方案,我们实现了以下几个目标:
从线上监控情况来看,目前的服务无论从响应时间、缓存命中率还是接口可用性来看,都比之前有了大幅提升。
作者: Sophos
链接: http://zhuanlan.zhihu.com/p/23326080
来源:知乎
著作权归作者所有,转载请联系作者获得授权。
1
mooncakejs 2016-10-31 20:14:28 +08:00 via iPhone
不怎么更新的我一般扔 cdn
|
2
sophos OP @mooncakejs 静态接口的确可以扔 cdn , fastcgi_cache 实际上也就是扮演这个角色,但动态接口还是得靠自己 : )
|
3
denghongcai 2016-11-01 11:00:45 +08:00
简单点讲:从完全使用外部缓存( Redis )改成了外部缓存和进程内缓存并用
|