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