一个优雅的|一个优雅的 LRU 缓存实现

Golang 的各种组件很灵活也很强大,但对于初级入门的使用者来说,要用好着实不易。最近,在开发一个可以拿来即用的 golang 库。第一个组件选择了缓存,主要是因为这个组件非常的关键,但也非常不容易实现好。
第一步:定义 Cache 接口
设计一个高扩展的缓存包,就需要利用 里氏替换原则(Liskov Substitution Principle),做好抽象,将缓存定义为接口

type Cache interface { ... }

第二步:组织包结构
然后,实现一个具体的 LRU 缓存,那么此时首先要组织好包结构,如下:
|-cache | |-lru | | |-lru.go | | |-segment.go | | |-options.go | | |-expvar.go |-cache.go

利用包划分层次,将接口放在根包下,作为所有子包的通用语言:
// cache.go package edge type Cache interface { ... }

第三步:实现 LRU 缓存
  1. 为了防止锁竞争导致的性能低下,此处使用分段加锁的方式降低锁粒度以提高缓存性能
  2. 同时将 segmentnewSegmentcache 以小写命名,避免对外暴露实现细节
  3. 使用 Higher-order function,实现可扩展的配置参数
  4. 使用 expvar 暴露缓存的状态
// lru.go package lrutype cache struct { ... } func New(opts ...Opt) (*cache, error) { ... }// segment.go package lrutype segment struct { ... } func newSegment(c int) *segment { ... }// options.go type options struct { ... }type Opt func(*options)func WithConcurrency(c int) Opt { return func(o *options) { o.concurrency = c } }func WithCapacity(c int) Opt { return func(o *options) { o.capacity = c } }// expvar.go var m = struct { Get*expvar.Int Set*expvar.Int Delete *expvar.Int Exists *expvar.Int Hit*expvar.Int Evict*expvar.Int }{ Get:expvar.NewInt("cache.lru.get"), Set:expvar.NewInt("cache.lru.set"), Delete: expvar.NewInt("cache.lru.delete"), Exists: expvar.NewInt("cache.lru.exists"), Hit:expvar.NewInt("cache.lru.hit"), Evict:expvar.NewInt("cache.lru.evict"), }

第四步:结束了么?
当然没有,从以上可以看到,以下几点:
  1. options 可以做到多种实现共用,更应该放在 cache 文件夹下。
  2. 在使用时,lru.New() 赋值给 Cache 接口略微不自然
  3. segment.go 和 expvar.go 未对使用者开放但文件却对外暴露
  4. segment 可能后续会用来实现其他缓存算法,也不适合放在 lru 包下
基于以上原因,再次调整包结构如下:
|-cache | |-options.go | |-lru.go |-cache.go |-internal | |-cache | | |-lru | | | |-expvar.go | | | |-segment.go

同时,调整 LRU 缓存的接口为:
// cache.go package cachetype cache struct { ... } func NewLRU(opts ...Opt) (*cache, error) { ... }

是不是自然了很多,使用示例:
var c edge.Cache = cache.NewLRU()

总结
学以致用,此 LRU 的实现应用了很多之前的知识。追求优秀代码的路是没有尽头的,下课。
源代码:github.com/cyningsun/edge
【一个优雅的|一个优雅的 LRU 缓存实现】本文作者:cyningsun
本文地址: https://www.cyningsun.com/07-...
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!

    推荐阅读