gRPC(Go)教程(零)---使用gRPC时遇到的问题
Contents
本文章主要记录在使用gRPC
时遇到的一些问题及其解决方案。
1. Method Not Found
1. 错误描述
在使用gPRC
时遇到的问题。
其中客户端由golang
编写,服务端由python
编写。
测试时一直报如下这个错:
rpc error:Code=Unimplemented desc = Method Not Found!
下面是官网上的错误列表,其中GRPC_STATUS_UNIMPLEMENTED
对应的case也是Method not found on server
。
Case | Status code |
---|---|
Client application cancelled the request | GRPC_STATUS_CANCELLED |
Deadline expired before server returned status | GRPC_STATUS_DEADLINE_EXCEEDED |
Method not found on server | GRPC_STATUS_UNIMPLEMENTED |
Server shutting down | GRPC_STATUS_UNAVAILABLE |
Server threw an exception (or did something other than returning a status code to terminate the RPC) | GRPC_STATUS_UNKNOWN |
但是 单独测试时
python
自己写的客户端服务端可以正常交互
golang
写的客户端 服务端也可以正常交互。
就是两个互相调用时会出现这个错误Method Not Found
仔细检查代码之后并没有什么问题。
各种google
之后总算找到了原因。
https://www.itread01.com/content/1547029280.html
2. 原因
这是由于
.proto文件中的
package name 被修改,和 server 端的package 不一致导致的,双方同步
.proto文件 packagename
重新编译生成对应的代码即可。
由于python写时没有加package xxx;
这就 然后go这边加了。。。怪不得会出错,果然在修改.proto文件
从新编译后就能成功运行了。
2. 数据传输限制
1. 错误描述
details = "Received message larger than max (6194304 vs. 4194304)"
2. 原因
接收消息超过了最大限制(默认4M)
3. 解决方案
可以在建立连接的时候修改这个限制。
server
maxSize := 20 * 1024 * 1024
s := grpc.NewServer(grpc.MaxRecvMsgSize(maxSize), grpc.MaxSendMsgSize(maxSize))
PbDownMaxSize.RegisterPbDownMaxSizeServer(s, &server{})
s.Serve(listener)
client
maxSize := 20 * 1024 * 1024
diaOpt := grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxSize), grpc.MaxCallSendMsgSize(maxSize))
conn, err := grpc.Dial(endpoint, grpc.WithInsecure(), diaOpt)
4. 相关源码
RecvMsg
func (p *parser) recvMsg(maxReceiveMessageSize int) (pf payloadFormat, msg []byte, err error) {
if _, err := p.r.Read(p.header[:]); err != nil {
return 0, nil, err
}
pf = payloadFormat(p.header[0])
length := binary.BigEndian.Uint32(p.header[1:])
if length == 0 {
return pf, nil, nil
}
if int64(length) > int64(maxInt) {
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max length allowed on current machine (%d vs. %d)", length, maxInt)
}
if int(length) > maxReceiveMessageSize {
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", length, maxReceiveMessageSize)
}
// TODO(bradfitz,zhaoq): garbage. reuse buffer after proto decoding instead
// of making it for each message:
msg = make([]byte, int(length))
if _, err := p.r.Read(msg); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return 0, nil, err
}
return pf, msg, nil
}
可以看到在Recv的时候判定了两次数据长度。
除了设置的长度外还有一个最大长度限制int64(length) > int64(maxInt)
if int64(length) > int64(maxInt) {
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max length allowed on current machine (%d vs. %d)", length, maxInt)
}
if int(length) > maxReceiveMessageSize {
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", length, maxReceiveMessageSize)
}
最大限制const maxInt = int(^uint(0) >> 1)
应该是2G大小。
不会有人拿gPRC传这么大的数据吧…
SendMsg
func (cs *clientStream) SendMsg(m interface{}) (err error) {
defer func() {
if err != nil && err != io.EOF {
// Call finish on the client stream for errors generated by this SendMsg
// call, as these indicate problems created by this client. (Transport
// errors are converted to an io.EOF error in csAttempt.sendMsg; the real
// error will be returned from RecvMsg eventually in that case, or be
// retried.)
cs.finish(err)
}
}()
if cs.sentLast {
return status.Errorf(codes.Internal, "SendMsg called after CloseSend")
}
if !cs.desc.ClientStreams {
cs.sentLast = true
}
// load hdr, payload, data
hdr, payload, data, err := prepareMsg(m, cs.codec, cs.cp, cs.comp)
if err != nil {
return err
}
// TODO(dfawley): should we be checking len(data) instead?
if len(payload) > *cs.callInfo.maxSendMessageSize {
return status.Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(payload), *cs.callInfo.maxSendMessageSize)
}
msgBytes := data // Store the pointer before setting to nil. For binary logging.
op := func(a *csAttempt) error {
err := a.sendMsg(m, hdr, payload, data)
// nil out the message and uncomp when replaying; they are only needed for
// stats which is disabled for subsequent attempts.
m, data = nil, nil
return err
}
err = cs.withRetry(op, func() { cs.bufferForRetryLocked(len(hdr)+len(payload), op) })
if cs.binlog != nil && err == nil {
cs.binlog.Log(&binarylog.ClientMessage{
OnClientSide: true,
Message: msgBytes,
})
}
return
}
同理发送的时候也有这个限制
if len(payload) > *cs.callInfo.maxSendMessageSize {
return status.Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(payload), *cs.callInfo.maxSendMessageSize)
}