day7 使用 Protobuf 通信
文章目录
- 1 为什么要使用 protobuf
- 2 使用 protobuf 通信
- 总结
本文代码地址: https://gitee.com/lymgoforIT/gee-cache/tree/master/day7-proto-buf
本文是7天用Go从零实现分布式缓存GeeCache
的第七篇。
- 为什么要使用
protobuf
? - 使用
protobuf
进行节点间通信,编码报文,提高效率。代码约50
行
1 为什么要使用 protobuf
protobuf
即 Protocol Buffers
,Google
开发的一种数据描述语言,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。protobuf
以二进制方式存储,占用空间小。
protobuf
广泛地应用于远程过程调用(RPC
) 的二进制传输,使用 protobuf
的目的非常简单,为了获得更高的性能。传输前使用 protobuf
编码,接收方再进行解码,可以显著地降低二进制传输的大小。另外一方面,protobuf
可非常适合传输结构化数据,便于通信字段的扩展。
使用 protobuf
一般分为以下 2
步:
- 按照
protobuf
的语法,在.proto
文件中定义数据结构,并使用protoc
生成Go
代码(.proto
文件是跨平台的,还可以生成C、Java
等其他源码文件)。 - 在项目代码中引用生成的
Go
代码。
2 使用 protobuf 通信
新建 package geecachepb
,定义 geecachepb.proto
day7-proto-buf/geecache/geecachepb/geecachepb.proto
syntax = "proto3";package geecachepb;
option go_package = "./";message Request {string group = 1;string key = 2;
}message Response {bytes value = 1;
}service GroupCache {rpc Get(Request) returns (Response);
}
Request
包含2
个字段,group
和cache
,这与我们之前定义的接口/_geecache/<group>/<name>
所需的参数吻合。Response
包含1
个字段,bytes
,类型为byte
数组,与之前吻合。
生成 geecache.pb.go
$ protoc --go_out=. *.proto
$ ls
geecachepb.pb.go geecachepb.proto
可以看到 geecachepb.pb.go
中有如下数据类型:
type Request struct {Group string `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"`Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`...
}
type Response struct {Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
}
接下来,修改 peers.go
中的 PeerGetter
接口,参数使用 geecachepb.pb.go
中的数据类型。
day7-proto-buf/geecache/peers.go
import pb "geecache/geecachepb"type PeerGetter interface {Get(in *pb.Request, out *pb.Response) error
}
最后,修改 geecache.go
和 http.go
中使用了 PeerGetter
接口的地方。
day7-proto-buf/geecache/geecache.go
import (// ...pb "geecache/geecachepb"
)func (g *Group) getFromPeer(peer PeerGetter, key string) (ByteView, error) {req := &pb.Request{Group: g.name,Key: key,}res := &pb.Response{}err := peer.Get(req, res)if err != nil {return ByteView{}, err}return ByteView{b: res.Value}, nil
}
day7-proto-buf/geecache/http.go
import (// ...pb "geecache/geecachepb""github.com/golang/protobuf/proto"
)func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {// ...// Write the value to the response body as a proto message.body, err := proto.Marshal(&pb.Response{Value: view.ByteSlice()})if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}w.Header().Set("Content-Type", "application/octet-stream")w.Write(body)
}func (h *httpGetter) Get(in *pb.Request, out *pb.Response) error {u := fmt.Sprintf("%v/%v/%v",h.baseURL,url.QueryEscape(in.GetGroup()),url.QueryEscape(in.GetKey()),)res, err := http.Get(u)// ...if err = proto.Unmarshal(bytes, out); err != nil {return fmt.Errorf("decoding response body: %v", err)}return nil
}
ServeHTTP()
中使用proto.Marshal()
编码HTTP
响应。Get()
中使用proto.Unmarshal()
解码HTTP
响应。
至此,我们已经将 HTTP
通信的中间载体替换成了 protobuf
。运行 run.sh
即可以测试 GeeCache
能否正常工作。
总结
到这一篇为止,7天用 Go 从零实现分布式缓存 GeeCache
这个系列就完成了。简单回顾下。
- 第一天:为了解决资源限制的问题,实现了
LRU
缓存淘汰算法 - 第二天:实现了单机并发,并给用户提供了自定义数据源的回调函数
- 第三天:新增了
HTTPPool
,为其实现了HTTP
服务端 - 第四天:实现了一致性哈希算法,解决远程节点的挑选问题
- 第五天:为
HTTPPool
增加了HTTP
客户端功能,实现了多节点间的通信 - 第六天:实现了
singleflight
解决缓存击穿的问题 - 第七天:使用
protobuf
库,优化了节点间通信的性能
原文地址:https://geektutu.com/post/geecache-day7.html