美文网首页
groupcache 源码系列四 Sink ByteView

groupcache 源码系列四 Sink ByteView

作者: 合肥黑 | 来源:发表于2019-03-25 17:52 被阅读0次

    在前面的源码系列中,学习了一些独立的知识点。下面先看一个使用示例,来更好地了解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

          本文链接:https://www.haomeiwen.com/subject/mlqbvqtx.html