GroupCache 是一款分布式缓存组件,最早被用于 dl.google.com 的文件下载。代码短小精炼,设计思想也值得学习。

组件特点

  • 分布式缓存,通过 HTTP 协议达成节点之间通信,用功能的取舍保证一致性
  • 只有读操作,在第一次cache miss时候回调写入数据,不可更改,没有过期时间,意味着只适用于存储 immutable 的数据,例如文件
  • singleflight 防止缓存穿透,多个相同的请求只有一个会打倒后端,避免惊群效应
  • 一致性hash做数据分片,LRU做数据逐出,pb做传输格式

面向接口编程

代码中的各个组件都体现了面向接口编程的优秀实践,核心参考Group结构体:

  • ByteView:
    • []byte&string封装,缓存返回的value结构
  • Sink:
    • 负责从一个Get调用中接收缓存数据,对接ByteView,从ByteView中获取数据,起到一个中间人的作用,和ByteView的关系有点类似DO和DTO/VO的关系
    • groupcache提供了不同的Sink实现类来满足不同数据格式的要求,内置的有string、byte和proto
  • Group
    • 标识一个唯一的缓存的命名空间,暴露Get接口
    • 维护对缓存各种操作和结果的统计信息
    • 缓存的写入和缓存的维护,包括本地的缓存,以及一些hot cache的维护(目前这一块不是很完美,只是一个简单的随机将从其他peer中获取的缓存写入hot cache中,命中率10%)
  • PeerPicker
    • 根据key获取缓存对应的Peer
  • HTTPPool
    • 简单的Http server/client,groupcache默认的peer选择器
  • flightGroup
    • singleflight.Do(),防止缓存击穿
  • lru.Cache
    • 基于Map构建的一个缓存结构,采用LRU算法进行缓存淘汰
  • cache
    • lru.Cache + 读写锁 + 统计信息,相当于是lru.Cache的代理结构,统计了缓存命中次数、Get请求次数以及缓存逐出次数等

组件关系图如下:

UML

核心流程

Get操作是整个groupcache的最核心的流程,具体流程为:

  1. 初始化PeerPicker实例(sync.Once保证只初始化一次),并统计Get请求次数
  2. 读本地cache,统计命中数据(如有)
  3. 读 peer cache,写入本地 cache(针对同一key的请求涌入情况,使用singleflight防止缓存击穿,保证只有一个请求发起remote cache的get请求)
  4. 缓存无数据,发起回调函数向后端取数据,并写入本地缓存

使用示例

TBC…