gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/benchmark/benchmark.go (about) 1 /* 2 * 3 * Copyright 2014 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 /* 20 Package benchmark implements the building blocks to setup end-to-end gRPC benchmarks. 21 */ 22 package benchmark 23 24 import ( 25 "context" 26 "fmt" 27 "io" 28 "log" 29 "net" 30 31 grpc "gitee.com/ks-custle/core-gm/grpc" 32 "gitee.com/ks-custle/core-gm/grpc/codes" 33 "gitee.com/ks-custle/core-gm/grpc/grpclog" 34 "gitee.com/ks-custle/core-gm/grpc/metadata" 35 "gitee.com/ks-custle/core-gm/grpc/status" 36 37 testgrpc "gitee.com/ks-custle/core-gm/grpc/interop/grpc_testing" 38 testpb "gitee.com/ks-custle/core-gm/grpc/interop/grpc_testing" 39 ) 40 41 var logger = grpclog.Component("benchmark") 42 43 // Allows reuse of the same testpb.Payload object. 44 func setPayload(p *testpb.Payload, t testpb.PayloadType, size int) { 45 if size < 0 { 46 logger.Fatalf("Requested a response with invalid length %d", size) 47 } 48 body := make([]byte, size) 49 switch t { 50 case testpb.PayloadType_COMPRESSABLE: 51 default: 52 logger.Fatalf("Unsupported payload type: %d", t) 53 } 54 p.Type = t 55 p.Body = body 56 } 57 58 // NewPayload creates a payload with the given type and size. 59 func NewPayload(t testpb.PayloadType, size int) *testpb.Payload { 60 p := new(testpb.Payload) 61 setPayload(p, t, size) 62 return p 63 } 64 65 type testServer struct { 66 testgrpc.UnimplementedBenchmarkServiceServer 67 } 68 69 func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { 70 return &testpb.SimpleResponse{ 71 Payload: NewPayload(in.ResponseType, int(in.ResponseSize)), 72 }, nil 73 } 74 75 // UnconstrainedStreamingHeader indicates to the StreamingCall handler that its 76 // behavior should be unconstrained (constant send/receive in parallel) instead 77 // of ping-pong. 78 const UnconstrainedStreamingHeader = "unconstrained-streaming" 79 80 func (s *testServer) StreamingCall(stream testgrpc.BenchmarkService_StreamingCallServer) error { 81 if md, ok := metadata.FromIncomingContext(stream.Context()); ok && len(md[UnconstrainedStreamingHeader]) != 0 { 82 return s.UnconstrainedStreamingCall(stream) 83 } 84 response := &testpb.SimpleResponse{ 85 Payload: new(testpb.Payload), 86 } 87 in := new(testpb.SimpleRequest) 88 for { 89 // use ServerStream directly to reuse the same testpb.SimpleRequest object 90 err := stream.(grpc.ServerStream).RecvMsg(in) 91 if err == io.EOF { 92 // read done. 93 return nil 94 } 95 if err != nil { 96 return err 97 } 98 setPayload(response.Payload, in.ResponseType, int(in.ResponseSize)) 99 if err := stream.Send(response); err != nil { 100 return err 101 } 102 } 103 } 104 105 func (s *testServer) UnconstrainedStreamingCall(stream testgrpc.BenchmarkService_StreamingCallServer) error { 106 in := new(testpb.SimpleRequest) 107 // Receive a message to learn response type and size. 108 err := stream.RecvMsg(in) 109 if err == io.EOF { 110 // read done. 111 return nil 112 } 113 if err != nil { 114 return err 115 } 116 117 response := &testpb.SimpleResponse{ 118 Payload: new(testpb.Payload), 119 } 120 setPayload(response.Payload, in.ResponseType, int(in.ResponseSize)) 121 122 go func() { 123 for { 124 // Using RecvMsg rather than Recv to prevent reallocation of SimpleRequest. 125 err := stream.RecvMsg(in) 126 switch status.Code(err) { 127 case codes.Canceled: 128 return 129 case codes.OK: 130 default: 131 log.Fatalf("server recv error: %v", err) 132 } 133 } 134 }() 135 136 go func() { 137 for { 138 err := stream.Send(response) 139 switch status.Code(err) { 140 case codes.Unavailable: 141 return 142 case codes.OK: 143 default: 144 log.Fatalf("server send error: %v", err) 145 } 146 } 147 }() 148 149 <-stream.Context().Done() 150 return stream.Context().Err() 151 } 152 153 // byteBufServer is a gRPC server that sends and receives byte buffer. 154 // The purpose is to benchmark the gRPC performance without protobuf serialization/deserialization overhead. 155 type byteBufServer struct { 156 testgrpc.UnimplementedBenchmarkServiceServer 157 respSize int32 158 } 159 160 // UnaryCall is an empty function and is not used for benchmark. 161 // If bytebuf UnaryCall benchmark is needed later, the function body needs to be updated. 162 func (s *byteBufServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { 163 return &testpb.SimpleResponse{}, nil 164 } 165 166 func (s *byteBufServer) StreamingCall(stream testgrpc.BenchmarkService_StreamingCallServer) error { 167 for { 168 var in []byte 169 err := stream.(grpc.ServerStream).RecvMsg(&in) 170 if err == io.EOF { 171 return nil 172 } 173 if err != nil { 174 return err 175 } 176 out := make([]byte, s.respSize) 177 if err := stream.(grpc.ServerStream).SendMsg(&out); err != nil { 178 return err 179 } 180 } 181 } 182 183 // ServerInfo contains the information to create a gRPC benchmark server. 184 type ServerInfo struct { 185 // Type is the type of the server. 186 // It should be "protobuf" or "bytebuf". 187 Type string 188 189 // Metadata is an optional configuration. 190 // For "protobuf", it's ignored. 191 // For "bytebuf", it should be an int representing response size. 192 Metadata interface{} 193 194 // Listener is the network listener for the server to use 195 Listener net.Listener 196 } 197 198 // StartServer starts a gRPC server serving a benchmark service according to info. 199 // It returns a function to stop the server. 200 func StartServer(info ServerInfo, opts ...grpc.ServerOption) func() { 201 opts = append(opts, grpc.WriteBufferSize(128*1024)) 202 opts = append(opts, grpc.ReadBufferSize(128*1024)) 203 s := grpc.NewServer(opts...) 204 switch info.Type { 205 case "protobuf": 206 testgrpc.RegisterBenchmarkServiceServer(s, &testServer{}) 207 case "bytebuf": 208 respSize, ok := info.Metadata.(int32) 209 if !ok { 210 logger.Fatalf("failed to StartServer, invalid metadata: %v, for Type: %v", info.Metadata, info.Type) 211 } 212 testgrpc.RegisterBenchmarkServiceServer(s, &byteBufServer{respSize: respSize}) 213 default: 214 logger.Fatalf("failed to StartServer, unknown Type: %v", info.Type) 215 } 216 go s.Serve(info.Listener) 217 return func() { 218 s.Stop() 219 } 220 } 221 222 // DoUnaryCall performs an unary RPC with given stub and request and response sizes. 223 func DoUnaryCall(tc testgrpc.BenchmarkServiceClient, reqSize, respSize int) error { 224 pl := NewPayload(testpb.PayloadType_COMPRESSABLE, reqSize) 225 req := &testpb.SimpleRequest{ 226 ResponseType: pl.Type, 227 ResponseSize: int32(respSize), 228 Payload: pl, 229 } 230 if _, err := tc.UnaryCall(context.Background(), req); err != nil { 231 return fmt.Errorf("/BenchmarkService/UnaryCall(_, _) = _, %v, want _, <nil>", err) 232 } 233 return nil 234 } 235 236 // DoStreamingRoundTrip performs a round trip for a single streaming rpc. 237 func DoStreamingRoundTrip(stream testgrpc.BenchmarkService_StreamingCallClient, reqSize, respSize int) error { 238 pl := NewPayload(testpb.PayloadType_COMPRESSABLE, reqSize) 239 req := &testpb.SimpleRequest{ 240 ResponseType: pl.Type, 241 ResponseSize: int32(respSize), 242 Payload: pl, 243 } 244 if err := stream.Send(req); err != nil { 245 return fmt.Errorf("/BenchmarkService/StreamingCall.Send(_) = %v, want <nil>", err) 246 } 247 if _, err := stream.Recv(); err != nil { 248 // EOF is a valid error here. 249 if err == io.EOF { 250 return nil 251 } 252 return fmt.Errorf("/BenchmarkService/StreamingCall.Recv(_) = %v, want <nil>", err) 253 } 254 return nil 255 } 256 257 // DoByteBufStreamingRoundTrip performs a round trip for a single streaming rpc, using a custom codec for byte buffer. 258 func DoByteBufStreamingRoundTrip(stream testgrpc.BenchmarkService_StreamingCallClient, reqSize, respSize int) error { 259 out := make([]byte, reqSize) 260 if err := stream.(grpc.ClientStream).SendMsg(&out); err != nil { 261 return fmt.Errorf("/BenchmarkService/StreamingCall.(ClientStream).SendMsg(_) = %v, want <nil>", err) 262 } 263 var in []byte 264 if err := stream.(grpc.ClientStream).RecvMsg(&in); err != nil { 265 // EOF is a valid error here. 266 if err == io.EOF { 267 return nil 268 } 269 return fmt.Errorf("/BenchmarkService/StreamingCall.(ClientStream).RecvMsg(_) = %v, want <nil>", err) 270 } 271 return nil 272 } 273 274 // NewClientConn creates a gRPC client connection to addr. 275 func NewClientConn(addr string, opts ...grpc.DialOption) *grpc.ClientConn { 276 return NewClientConnWithContext(context.Background(), addr, opts...) 277 } 278 279 // NewClientConnWithContext creates a gRPC client connection to addr using ctx. 280 func NewClientConnWithContext(ctx context.Context, addr string, opts ...grpc.DialOption) *grpc.ClientConn { 281 opts = append(opts, grpc.WithWriteBufferSize(128*1024)) 282 opts = append(opts, grpc.WithReadBufferSize(128*1024)) 283 conn, err := grpc.DialContext(ctx, addr, opts...) 284 if err != nil { 285 logger.Fatalf("NewClientConn(%q) failed to create a ClientConn %v", addr, err) 286 } 287 return conn 288 }