From ed77b45338484232ecc97c71990985b789b8da0a Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Sun, 16 Jun 2019 21:43:05 +0200 Subject: [PATCH] Initial PoC commit with minimally working echo grpc server and client --- grpc-client.go | 114 +++++++++++++++++++++++++++++++++ grpc-server.go | 28 +++++++++ pb/service.pb.go | 161 +++++++++++++++++++++++++++++++++++++++++++++++ pb/service.proto | 12 ++++ 4 files changed, 315 insertions(+) create mode 100644 grpc-client.go create mode 100644 grpc-server.go create mode 100644 pb/service.pb.go create mode 100644 pb/service.proto diff --git a/grpc-client.go b/grpc-client.go new file mode 100644 index 0000000..1742900 --- /dev/null +++ b/grpc-client.go @@ -0,0 +1,114 @@ +package main + +import ( + "fmt" + "log" + "os" + "time" + + pb "github.com/smirnov/grpc-echo/pb" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +const ( + warmupRounds = 200 + rounds = 1000 +) + +var ( + address = "localhost:50051" +) + +func main() { + if len(os.Args) > 1 { + address = os.Args[1] + } + // Set up a connection to the server. + //var durations []time.Duration + + //Warmup run + min := time.Hour + max := time.Nanosecond + sum := time.Nanosecond * 0 + for i := 0; i < warmupRounds; i++ { + duration, err := establishConnectionAndCallRemote(address) + if err == nil { + //append(durations, duration) + if min > duration { + min = duration + } + if max < duration { + max = duration + } + sum = sum + duration + } + } + avg := sum / warmupRounds + fmt.Printf("Measurement run for %d warmup rounds\n", warmupRounds) + fmt.Printf("avg: %s min: %s max: %s\n", avg, min, max) + + //Repeat for measurement run + min = time.Hour + max = time.Nanosecond + sum = time.Nanosecond * 0 + for i := 0; i < rounds; i++ { + duration, err := establishConnectionAndCallRemote(address) + if err == nil { + //append(durations, duration) + if min > duration { + min = duration + } + if max < duration { + max = duration + } + sum = sum + duration + } + } + avg = sum / rounds + fmt.Printf("Measurement run for %d rounds with new connection for every call\n", rounds) + fmt.Printf("avg: %s min: %s max: %s\n", avg, min, max) + + //Repeat without re-establishing connection + min = time.Hour + max = time.Nanosecond + sum = time.Nanosecond * 0 + conn, err := grpc.Dial(address, grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + c := pb.NewEchoServiceClient(conn) + for i := 0; i < rounds; i++ { + start := time.Now() + _, err = c.Echo(context.Background(), &pb.Message{Msg: "1234"}) + end := time.Now() + if err == nil { + duration := end.Sub(start) + //append(durations, duration) + if min > duration { + min = duration + } + if max < duration { + max = duration + } + sum = sum + duration + } + } + avg = sum / rounds + fmt.Printf("Measurement run for %d rounds without re-establishing connection\n", rounds) + fmt.Printf("avg: %s min: %s max: %s\n", avg, min, max) +} + +func establishConnectionAndCallRemote(address string) (time.Duration, error) { + start := time.Now() + conn, err := grpc.Dial(address, grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %v\n Have you tried appending port ':50051'?", err) + } + defer conn.Close() + c := pb.NewEchoServiceClient(conn) + _, err = c.Echo(context.Background(), &pb.Message{Msg: "1234"}) + end := time.Now() + return end.Sub(start), err +} diff --git a/grpc-server.go b/grpc-server.go new file mode 100644 index 0000000..bbaa3ed --- /dev/null +++ b/grpc-server.go @@ -0,0 +1,28 @@ +package main + +import ( + context "context" + "net" + + pb "github.com/smirnov/grpc-echo/pb" + "google.golang.org/grpc" +) + +type EchoServer struct { +} + +//Simple echoing functionality returning inbound message as is +func (s EchoServer) Echo(ctx context.Context, inbound *pb.Message) (*pb.Message, error) { + return inbound, nil +} + +func main() { + echoServer := EchoServer{} + listen, err := net.Listen("tcp", ":50051") + if err != nil { + panic(err) + } + grpcServer := grpc.NewServer() + pb.RegisterEchoServiceServer(grpcServer, echoServer) + grpcServer.Serve(listen) +} diff --git a/pb/service.pb.go b/pb/service.pb.go new file mode 100644 index 0000000..e25e4c0 --- /dev/null +++ b/pb/service.pb.go @@ -0,0 +1,161 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: service.proto + +package echo + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type Message struct { + Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} +func (*Message) Descriptor() ([]byte, []int) { + return fileDescriptor_a0b84a42fa06f626, []int{0} +} + +func (m *Message) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Message.Unmarshal(m, b) +} +func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Message.Marshal(b, m, deterministic) +} +func (m *Message) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message.Merge(m, src) +} +func (m *Message) XXX_Size() int { + return xxx_messageInfo_Message.Size(m) +} +func (m *Message) XXX_DiscardUnknown() { + xxx_messageInfo_Message.DiscardUnknown(m) +} + +var xxx_messageInfo_Message proto.InternalMessageInfo + +func (m *Message) GetMsg() string { + if m != nil { + return m.Msg + } + return "" +} + +func init() { + proto.RegisterType((*Message)(nil), "echo.Message") +} + +func init() { proto.RegisterFile("service.proto", fileDescriptor_a0b84a42fa06f626) } + +var fileDescriptor_a0b84a42fa06f626 = []byte{ + // 112 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2d, 0x4e, 0x2d, 0x2a, + 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x49, 0x4d, 0xce, 0xc8, 0x57, + 0x92, 0xe6, 0x62, 0xf7, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f, 0x15, 0x12, 0xe0, 0x62, 0xce, 0x2d, + 0x4e, 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x8d, 0x4c, 0xb9, 0xb8, 0x5d, 0x93, + 0x33, 0xf2, 0x83, 0x21, 0xfa, 0x84, 0xd4, 0xb8, 0x58, 0x40, 0x5c, 0x21, 0x5e, 0x3d, 0x90, 0x56, + 0x3d, 0xa8, 0x3e, 0x29, 0x54, 0xae, 0x12, 0x83, 0x13, 0x5b, 0x14, 0xd8, 0xec, 0x24, 0x36, 0xb0, + 0x45, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe2, 0xee, 0xd8, 0x69, 0x79, 0x00, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// EchoServiceClient is the client API for EchoService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type EchoServiceClient interface { + Echo(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) +} + +type echoServiceClient struct { + cc *grpc.ClientConn +} + +func NewEchoServiceClient(cc *grpc.ClientConn) EchoServiceClient { + return &echoServiceClient{cc} +} + +func (c *echoServiceClient) Echo(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) { + out := new(Message) + err := c.cc.Invoke(ctx, "/echo.EchoService/Echo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// EchoServiceServer is the server API for EchoService service. +type EchoServiceServer interface { + Echo(context.Context, *Message) (*Message, error) +} + +// UnimplementedEchoServiceServer can be embedded to have forward compatible implementations. +type UnimplementedEchoServiceServer struct { +} + +func (*UnimplementedEchoServiceServer) Echo(ctx context.Context, req *Message) (*Message, error) { + return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented") +} + +func RegisterEchoServiceServer(s *grpc.Server, srv EchoServiceServer) { + s.RegisterService(&_EchoService_serviceDesc, srv) +} + +func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Message) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EchoServiceServer).Echo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/echo.EchoService/Echo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EchoServiceServer).Echo(ctx, req.(*Message)) + } + return interceptor(ctx, in, info, handler) +} + +var _EchoService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "echo.EchoService", + HandlerType: (*EchoServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Echo", + Handler: _EchoService_Echo_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "service.proto", +} diff --git a/pb/service.proto b/pb/service.proto new file mode 100644 index 0000000..b77f95b --- /dev/null +++ b/pb/service.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +option go_package = "echo"; + +package echo; + +message Message { + string msg = 1; +} + +service EchoService { + rpc Echo(Message) returns (Message) {} +} \ No newline at end of file