groupcache 源码系列四 Sink ByteView

懒皮 · · 675 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

在前面的源码系列中,学习了一些独立的知识点。下面先看一个使用示例,来更好地了解gropucache流程。
如果不熟悉http基础知识,可以先参考Golang http.Handler接口

本文参考
grgoupcache源码走读(三):groupcache的特性和使用案例
ByteView和Sink

一、不设置集群peers的简单版

这里节选一部分代码

func main() {
    ...
    //初始化本地groupcache, 并监听groupcache相应的端口
    setUpGroup("test_cache",*group_address)
    ...
}   

    //启动groupcache
func setUpGroup(name string, groupAddr string) {
    //缓存池,
    stringGroup := groupcache.NewGroup(name, 1<<20,
        groupcache.GetterFunc(func(_ groupcache.Context, key string, dest groupcache.Sink) error {
            //当cache miss之后,用来执行的load data方法
            fp, err := os.Open("groupcache.conf")
            if err != nil {
                return err
            }
            defer fp.Close()

            fmt.Printf("look up for %s from config_file\n", key)
            //按行读取配置文件
            buf := bufio.NewReader(fp)
            for {
                line, err := buf.ReadString('\n')

                if err != nil {
                    if err == io.EOF {
                        dest.SetBytes([]byte{})
                        return errors.New("not found")
                        //return nil
                    } else {
                        return err
                    }
                }
                line = strings.TrimSpace(line)
                parts := strings.Split(line, "=")
                if len(parts) > 2 {
                    continue
                } else if parts[0] == key {
                    dest.SetBytes([]byte(parts[1]))
                    return nil
                } else {
                    continue
                }
            }
        }))

    http.HandleFunc("/config", func(rw http.ResponseWriter, r *http.Request) {
        k := r.URL.Query().Get("key")
        var dest []byte
        fmt.Printf("look up for %s from groupcache\n", k)
        if err := stringGroup.Get(nil, k, groupcache.AllocatingByteSliceSink(&dest)); err != nil {
            rw.WriteHeader(http.StatusNotFound)
            rw.Write([]byte("this key doesn't exists"))
        } else {
            rw.Write([]byte(dest))
        }
    })

    //能够直接访问cache的端口, 启动http服务
    //http://ip:group_addr/config?key=xxx
    go http.ListenAndServe(groupAddr, nil)
}

这里groupaddr可以使用flag来传入,比如group_addr = ":8081",然后就可以使用浏览器访问http://127.0.0.1:8082/config?key=price了。groupcache.conf是在缓存没有的时候,从文件中读取,随便写一下:

name=apple
price=12
from=beijing

这里控制台第一次就会打印出

look up for price from groupcache
look up for price from config_file

也就是从groupcache中没找到,又去文件里找。如果刷新浏览器,就不会再打印从文件里找了,这说明缓存里已经有数据了。

撇开http的代码,可以注意到stringGroup.Get(nil, k, groupcache.AllocatingByteSliceSink(&dest)),也就是说会把请求查询的字符串k传进去,把查的结果写到dest里。至于怎么查,就是上面的定义:

    stringGroup := groupcache.NewGroup(name, 1<<20,
        groupcache.GetterFunc(func(_ groupcache.Context, key string, dest groupcache.Sink) error {

NewGroup传入了名字,缓存大小(1<<20表示是1M),还有缓存里没有的话怎么查。

1.Sink
上面代码中,看到dest groupcache.Sink,还有dest.SetBytes([]byte(parts[1])),看一下Sink的源码

// A Sink receives data from a Get call.
//
// Implementation of Getter must call exactly one of the Set methods
// on success.
type Sink interface {
    // SetString sets the value to s.
    SetString(s string) error

    // SetBytes sets the value to the contents of v.
    // The caller retains ownership of v.
    SetBytes(v []byte) error

    // SetProto sets the value to the encoded version of m.
    // The caller retains ownership of m.
    SetProto(m proto.Message) error

    // view returns a frozen view of the bytes for caching.
    view() (ByteView, error)
}

可以看到实现了Sink的结构体应该是用来存储某种数据的,存的时候需要调用到SetXxx()方法,可以通过view()方法获取到一些东西,返回值是ByteView类型的。先看一个最简单的stringSink结构体:

// StringSink returns a Sink that populates the provided string pointer.
func StringSink(sp *string) Sink {
    return &stringSink{sp: sp}
}

type stringSink struct {
    sp *string
    v  ByteView
    // TODO(bradfitz): track whether any Sets were called.
}

func (s *stringSink) view() (ByteView, error) {
    // TODO(bradfitz): return an error if no Set was called
    return s.v, nil
}

func (s *stringSink) SetString(v string) error {
    s.v.b = nil
    s.v.s = v
    *s.sp = v
    return nil
}

func (s *stringSink) SetBytes(v []byte) error {
    return s.SetString(string(v))
}

func (s *stringSink) SetProto(m proto.Message) error {
    b, err := proto.Marshal(m)
    if err != nil {
        return err
    }
    s.v.b = b
    *s.sp = string(b)
    return nil
}

这里不可避免地要看一下ByteView的源码了

2.ByteView
byteview.go文件封装了一个string与byte[]的统一接口,也就是说用byteview提供的接口,可以屏蔽掉string与byte[]的不同,使用时可以不用考虑是string还是byte[]。

// A ByteView holds an immutable view of bytes.
// Internally it wraps either a []byte or a string,
// but that detail is invisible to callers.
//
// A ByteView is meant to be used as a value type, not
// a pointer (like a time.Time).
type ByteView struct {
    // If b is non-nil, b is used, else s is used.
    b []byte
    s string
}

然后提供了一系列方法:

// 返回长度
func (v ByteView) Len() int

// 按[]byte返回一个拷贝
func (v ByteView) ByteSlice() []byte

// 按string返回一个拷贝
func (v ByteView) String() string 

// 返回第i个byte
func (v ByteView) At(i int) byte

// 返回ByteView的某个片断,不拷贝
func (v ByteView) Slice(from, to int) ByteView

// 返回ByteView的从某个位置开始的片断,不拷贝
func (v ByteView) SliceFrom(from int) ByteView

// 将ByteView按[]byte拷贝出来
func (v ByteView) Copy(dest []byte) int

// 判断2个ByteView是否相等
func (v ByteView) Equal(b2 ByteView) bool

// 判断ByteView是否和string相等
func (v ByteView) EqualString(s string) bool

// 判断ByteView是否和[]byte相等
func (v ByteView) EqualBytes(b2 []byte) bool

// 对ByteView创建一个io.ReadSeeker
func (v ByteView) Reader() io.ReadSeeker

// 读取从off开始的后面的数据,其实下面调用的SliceFrom,这是封装成了io.Reader的一个ReadAt方法的形式
func (v ByteView) ReadAt(p []byte, off int64) (n int, err error)

//向w流中写入v
func (v ByteView) WriteTo(w io.Writer) (n int64, err error)

3.其他Sink,比如上面用的AllocatingByteSliceSink,剩下的不再粘贴

// AllocatingByteSliceSink returns a Sink that allocates
// a byte slice to hold the received value and assigns
// it to *dst. The memory is not retained by groupcache.
//【分配一个字节切片来保存接收到的数据】
func AllocatingByteSliceSink(dst *[]byte) Sink {
    return &allocBytesSink{dst: dst}
}

//【有一个字节切片指针类型的属性dst】
type allocBytesSink struct {
    dst *[]byte
    v   ByteView
}

func (s *allocBytesSink) view() (ByteView, error) {
    return s.v, nil
}

//【设置allocBytesSink的v,同时复制v中的b或者s丢给dst】
func (s *allocBytesSink) setView(v ByteView) error {
    if v.b != nil {
        *s.dst = cloneBytes(v.b)
    } else {
        *s.dst = []byte(v.s)
    }
    s.v = v
    return nil
}

//【这个得从下面的setBytesOwned开始往上看】
func (s *allocBytesSink) SetProto(m proto.Message) error {
    b, err := proto.Marshal(m)
    if err != nil {
        return err
    }
    return s.setBytesOwned(b)
}

//【复制一份b,然后调用setBytesOwned】
func (s *allocBytesSink) SetBytes(b []byte) error {
    return s.setBytesOwned(cloneBytes(b))
}

//【使用b设置allocBytesSink的dst和ByteView】
func (s *allocBytesSink) setBytesOwned(b []byte) error {
    if s.dst == nil {
        return errors.New("nil AllocatingByteSliceSink *[]byte dst")
    }
    *s.dst = cloneBytes(b) // another copy, protecting the read-only s.v.b view
    s.v.b = b
    s.v.s = ""
    return nil
}

//【字符串转成[]byte后进行和上面类似的操作】
func (s *allocBytesSink) SetString(v string) error {
    if s.dst == nil {
        return errors.New("nil AllocatingByteSliceSink *[]byte dst")
    }
    *s.dst = []byte(v)
    s.v.b = nil
    s.v.s = v
    return nil
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:懒皮

查看原文:groupcache 源码系列四 Sink ByteView

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

675 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传