问题描述
在gRPC进行服务间通信时,发送了一个较大的消息,出现 "grpc: received message larger than max" 的错误。
原因
gRPC 客户端和服务端都有一个默认的最大消息大小限制,默认为4MB, gRPC在发送或接收的消息大小超过最大限制时,就会抛出这个错误,导致通信失败。其相關源碼如下:
func (p *parser) recvMsg(maxReceiveMessageSize int) (pf payloadFormat, msg []byte, err error) {
...
if int(length) > maxReceiveMessageSize {
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", length, maxReceiveMessageSize)
}
...
问题重现
- 创建一个
greeter.proto
,参考链接https://github.com/grpc/grpc-go/tree/master/examples/helloworld
syntax = "proto3";
package helloworld.v1;
import "google/api/annotations.proto";
option go_package = "helloworld/api/helloworld/v1;v1";
option java_multiple_files = true;
option java_package = "dev.kratos.api.helloworld.v1";
option java_outer_classname = "HelloworldProtoV1";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/helloworld/{name}"
};
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
- 实现grpc服务端,创建文件grpc-server/main.go,详细如下:
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"google.golang.org/grpc"
pb "helloworld/api/helloworld/v1"
)
var (
port = flag.Int("port", 9000, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
启动grpc server
server listening at [::]:9000
实现grpc客户端,创建文件grpc-client/main.go,发送的消息大小超过了默认的最大限制(4MB),详细如下
package main
import (
"context"
"log"
"google.golang.org/grpc"
pb "helloworld/api/helloworld/v1"
)
func main() {
conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
//name := "world"
name := make([]byte, 10*1024*1024) // 10MB 的数据
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: string(name)})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
發送一個10MB的信息,執行输出:
could not greet: rpc error: code = ResourceExhausted desc = grpc: received message larger than max (10485765 vs. 4194304)
可預見問題重現了。
如何解决
通过修改增加 gRPC 客户端和服务端启动option (分别为grpc.ServerOption
、grpc.WithDefaultCallOptions
)来修改的最大消息大小限制
- grpc server的修改如下
grpcOpts := []grpc.ServerOption{
grpc.MaxRecvMsgSize(20 * 1024 * 1024), // 最大接收消息大小为 20MB
grpc.MaxSendMsgSize(20 * 1024 * 1024), // 最大发送消息大小为 20MB
}
s := grpc.NewServer(grpcOpts...)
- grpc client的修改
conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure(), grpc.WithBlock(),
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(20*1024*1024), // 设置最大接收消息为 20MB
grpc.MaxCallSendMsgSize(20*1024*1024), // 设置最大发送消息为 20MB
))
再次發送消息,输出如下:
Greeting: Hello
説明成功解决最大消息大小限制的问题。
kratos框架里如何修改grpc传输数据上限
kratos可以通过Options(opts ...grpc.ServerOption) ServerOption
配置一些额外的 grpc.ServerOption
import (
"github.com/go-kratos/kratos/v2/transport/grpc"
ggrpc "google.golang.org/grpc"
...
)
grpcOpts := []grpc.ServerOption{
grpc.Options(
ggrpc.MaxRecvMsgSize(20*1024*1024), // 最大接收消息大小为 20MB
ggrpc.MaxSendMsgSize(20*1024*1024), // 最大发送消息大小为 20MB
),
}
grpcClient可以修改grpc.CallOption来实现
opts := []grpc.CallOption{
grpc.MaxCallRecvMsgSize(20 * 1024 * 1024), // 最大接收消息大小为 20MB
grpc.MaxCallSendMsgSize(20 * 1024 * 1024), // 最大发送消息大小为 20MB
}
小结
本文主要介绍如何解决grpc通信时抛出grpc: received message larger than max
的错误,原因是消息大小超过了客户端和服务端默认的最大消息大小限制, 文中通过修改gRpc服务端、客户端的例子演示了如何復現、解决该问题。当然,在实际开发中,我们应该注意:1. 最大消息大小要根据业务场景,不宜过大或太小,避免浪费资源。2. 如果需要传输大数据,可以考虑流式Rpc来分块传输。 另外,在kratos中,可以通过配置一些额外的 grpc.ServerOption
和 grpc.CallOption
来修改。 希望对您有所帮助。
参考
- grpc.io grpc.io/docs/langua…
- github.com/grpc/grpc-go github.com/grpc/grpc-g…
- kratos grpc 配置 go-kratos.dev/docs/compon…