使用 GRPC 的 Go 实现时,在多个请求之间重用或共享空或不变的响应的最佳实践是什么?这节省了不必要的分配 - 但有什么理由不这样做呢?
例如:
var emptyPingResponse = &pb.PingResponse{}
var staticBuildInfo = &pb.GetBuildInfoResponse {
Version: /* ... */,
BuildDate: /* ... */,
}
func (*Service) Ping(ctx context.Context, req *pb.PingRequest) (res *pb.PingResponse, err error) {
// do something here
return emptyPingResponse, nil
}
func (*Service) GetBuildInfo(ctx context.Context, req *pb.GetBuildInfoRequest) (res *pb.GetBuildInfoResponse, err error) {
return staticBuildInfo, nil
}
我有一个想法,但我想讨论一下是否可行?
我使用sync.Pool来重用GRPC响应,因为我的响应数据太大了。
这是原创的
.pb.go
func (c *noticeServiceClient) Notice(ctx context.Context, in *NoticeRequest, opts ...grpc.CallOption) (*NoticeResponse, error) {
out := new(NoticeResponse)
err := c.cc.Invoke(ctx, NoticeService_Notice_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
我没有更改它,但我向这个包中添加了另一个 go 文件。 (它们在同一个包装中)
我将使用
NoticeWithPoolResponse
代替原来的 Notice
。package notice
import (
context "context"
grpc "google.golang.org/grpc"
sync "sync"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
var noticeResponsePool = sync.Pool{
New: func() interface{} {
return new(NoticeResponse)
},
}
type CustomNoticeServiceClient interface {
NoticeWithPoolResponse (ctx context.Context, in *NoticeRequest, opts ...grpc.CallOption) (*NoticeResponse, error)
}
func NewCustomNoticeServiceClient(cc grpc.ClientConnInterface) CustomNoticeServiceClient {
return ¬iceServiceClient{cc}
}
func resetNoticeResponse(r *NoticeResponse) {
// FIXME: check if clear builtin property is necessary
r.state = protoimpl.MessageState{}
r.sizeCache = 0
r.unknownFields = protoimpl.UnknownFields{}
// initial
r.ErrCode = 0
r.List = r.List[:0] // this is so bigggg
}
func (c *noticeServiceClient) NoticeWithPoolResponse(ctx context.Context, in *NoticeRequest, opts ...grpc.CallOption) (*NoticeResponse, error) {
out := noticeResponsePool.Get().(*NoticeResponse)
defer func () {
resetNoticeResponse(out)
noticeResponsePool.Put(out)
}()
err := c.cc.Invoke(ctx, NoticeService_Notice_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}