今天咱一次讲3个吧,赶一下进度,好早点开始聊kubernetes!

从groupcache的项目目录结构看,我们今天要学习groupcachepb、lru、singleflight这3个package:

 

一、protobuf

  这个目录咋一看有2个文件:go和proto后缀的。proto后缀的文件和protocol buffers有关,所以先看看protocol buffers是什么吧。

  在github上可以看到这个项目:https://github.com/google/protobuf

  google的,是不是瞬间来了兴趣?

  官方介绍是:Protocol Buffers (a.k.a., protobuf) are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.简单说就是跨语言跨平台的可拓展的结构数据序列化用的。翻译着有点别扭,还是直接看英文好理解。。。行,现在大家知道这个是用来做数据序列化的了,大家是否记得Golang自带的一个数据结构序列化编码/解码工具gob?之前我们有专门介绍过:《golang - gob与rpc》。

  ok,看过gob这篇文章,大家就知道protobuf需要解决的基本问题了,下面我们结合源码来看protobuf的知识点。

$GOPATHsrcgithub.comgolanggroupcachegroupcachepbgroupcache.proto内容如下:

 1syntax = "proto2";
 2
 3package groupcachepb;
 4
 5message GetRequest {
 6  required string group = 1;
 7  required string key = 2// not actually required/guaranteed to be UTF-8
 8}
 9
10message GetResponse {
11  optional bytes value = 1;
12  optional double minute_qps = 2;
13}
14
15service GroupCache {
16  rpc Get(GetRequest) returns (GetResponse) {
17  };
18}

  可以看到这是某种语法的数据定义格式,我们先介绍一下这里涉及的概念:

  protobuf中主要数据类型有:

  • 标准数据类型:整型,浮点,字符串等

  • 复合数据类型:枚举和message类型

  看message部分:

message GetResponse {
    optional bytes value = 1;
    optional double minute_qps = 2;
}

  • 每个字段末尾有一个tag,这个tag要求不重复,如这里的1、2;

  • 每个字段有一个类型,如这里的bytes、double;

  • 每个字段开头的optional含义为:

    • required: 必须赋值,不能为空

    • optional:可以赋值,也可以不赋值

    • repeated: 该字段可以重复任意次数,包括0次

  现在我们可以看懂这个message的名字是GetResponse,有2个可选字段value和minute_qps,两个字段的类型分别为bytes和double,2个字段都是optional的。

  protobuf也提供了包的定义,只要在文件开头定义package关键字即可,所以这里的package groupcachepb;这行也好理解;第一行syntax = "proto2";明显是声明版本的,除了proto2外还有proto3版本,类似与py2后有了py3。

  到这里就剩下最后几行有点疑惑了:

service GroupCache {

    rpc Get(GetRequest) returns (GetResponse) {

    };

}

  这里可以看到打头的是service,中间的字段是一个rpc相关的类似函数的东西,参数和返回值都是上面定义的message:GetRequestGetResponse,明显这里和rpc要有关系了,细节我们先不讲,到后面调用到的地方咱再结合业务代码来理解这里的细节。

 

二、LRU

  查一下百度百科,可以得到LRU的解释如下:

  内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。

什么是LRU算法? LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的。

  所以这里的lru包也就是用来实现lru算法的,详细的解释我放在注释中:$GOPATHsrcgithub.comgolanggroupcachelrulru.go:

  1// Package lru implements an LRU cache.
  2//【lru包用于实现LRU cache】
  3package lru
  4
  5import "container/list"
  6
  7// Cache is an LRU cache. It is not safe for concurrent access.
  8//【Cache结构用于实现LRU cache算法;并发访问不安全】
  9type Cache struct {
 10    // MaxEntries is the maximum number of cache entries before
 11    // an item is evicted. Zero means no limit.
 12    //【最大入口数,也就是缓存中最多存几条数据,超过了就触发数据淘汰;0表示没有限制】
 13    MaxEntries int
 14
 15    // OnEvicted optionally specificies a callback function to be
 16    // executed when an entry is purged from the cache.
 17    //【销毁前回调】
 18    OnEvicted func(key Key, value interface{})
 19
 20    //【链表】
 21    ll *list.List
 22    //【key为任意类型,值为指向链表一个结点的指针】
 23    cache map[interface{}]*list.Element
 24}
 25
 26// A Key may be any value that is comparable. 
 27// See http://golang.org/ref/spec#Comparison_operators
 28//【任意可比较类型】
 29type Key interface{}
 30
 31//【访问入口结构,包装键值】
 32type entry struct {
 33    key   Key
 34    value interface{}
 35}
 36
 37// New creates a new Cache.
 38// If maxEntries is zero, the cache has no limit and it's assumed
 39// that eviction is done by the caller.
 40//【初始化一个Cache类型实例】
 41func New(maxEntries int) *Cache {
 42    return &Cache{
 43        MaxEntries: maxEntries,
 44        ll:         list.New(),
 45        cache:      make(map[interface{}]*list.Element),
 46    }
 47}
 48
 49// Add adds a value to the cache.
 50//【往缓存中增加一个值】
 51func (c *Cache) Add(key Key, value interface{}) {
 52    //【如果Cache还没有初始化,先初始化,创建cache和l1】
 53    if c.cache == nil {
 54        c.cache = make(map[interface{}]*list.Element)
 55        c.ll = list.New()
 56    }
 57    //【如果key已经存在,则将记录前移到头部,然后设置value】
 58    if ee, ok := c.cache[key]; ok {
 59        c.ll.MoveToFront(ee)
 60        ee.Value.(*entry).value = value
 61        return
 62    }
 63    //【key不存在时,创建一条记录,插入链表头部,ele是这个Element的指针】
 64    //【这里的Element是一个*entry类型,ele是*list.Element类型】
 65    ele := c.ll.PushFront(&entry{key, value})
 66    //cache这个map设置key为Key类型的key,value为*list.Element类型的ele
 67    c.cache[key] = ele
 68    //【链表长度超过最大入口值,触发清理操作】
 69    if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
 70        c.RemoveOldest()
 71    }
 72}
 73
 74// Get looks up a key's value from the cache.
 75//【根据key查找value】
 76func (c *Cache) Get(key Key) (value interface{}, ok bool) {
 77    if c.cache == nil {
 78        return
 79    }
 80    //【如果存在】
 81    if ele, hit := c.cache[key]; hit {
 82        //【将这个Element移动到链表头部】
 83        c.ll.MoveToFront(ele)
 84        //【返回entry的值】
 85        return ele.Value.(*entry).value, true
 86    }
 87    return
 88}
 89
 90// Remove removes the provided key from the cache.
 91//【如果key存在,调用removeElement删除链表and缓存中的元素】
 92func (c *Cache) Remove(key Key) {
 93    if c.cache == nil {
 94        return
 95    }
 96    if ele, hit := c.cache[key]; hit {
 97        c.removeElement(ele)
 98    }
 99}
100
101// RemoveOldest removes the oldest item from the cache.
102//【删除最旧的元素】
103func (c *Cache) RemoveOldest() {
104    
收藏 打印