day7 使用 Protobuf 通信


本文代码地址: https://gitee.com/lymgoforIT/gee-cache/tree/master/day7-proto-buf

本文是7天用Go从零实现分布式缓存GeeCache的第七篇。

  • 为什么要使用 protobuf
  • 使用 protobuf 进行节点间通信,编码报文,提高效率。代码约50

1 为什么要使用 protobuf

protobufProtocol BuffersGoogle 开发的一种数据描述语言,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。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 个字段, groupcache,这与我们之前定义的接口 /_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.gohttp.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

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值