diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..35410cac --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/jrpc.iml b/.idea/jrpc.iml new file mode 100644 index 00000000..5e764c4f --- /dev/null +++ b/.idea/jrpc.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..671b9ade --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..0a1ee021 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..b839f240 --- /dev/null +++ b/go.mod @@ -0,0 +1,22 @@ +module jrpc + +go 1.22.2 + +//replace google.golang.org/grpc => ./third_party/grpc-go +// +//require ( +// google.golang.org/grpc v1.0.0 +//) + +require ( + github.com/gofrs/uuid v4.4.0+incompatible + google.golang.org/grpc v1.63.2 +) + +require ( + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/protobuf v1.33.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..0ee33ad8 --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/src/CF.go b/src/CF.go new file mode 100644 index 00000000..c86fbee4 --- /dev/null +++ b/src/CF.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("555") +} diff --git a/src/client/main.go b/src/client/main.go new file mode 100644 index 00000000..f95eecd3 --- /dev/null +++ b/src/client/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "context" + pb "jrpc/src/pb" + "log" + + "google.golang.org/grpc" +) + +const ( + // 服务端地址 + address = "localhost:50051" +) + +func main() { + // 创建 gRPC 连接 + conn, err := grpc.Dial(address, grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + + // 创建客户端 stub,利用它调用远程方法 + c := pb.NewProductInfoClient(conn) + + name := "XiaoMi 11" + description := "XiaoMi 11 with MIUI 12.5" + price := float32(3999.00) + + // 调用远程方法 + r, err := c.AddProduct(context.Background(), &pb.Product{Name: name, Description: description, Price: price}) + if err != nil { + log.Fatalf("Could not add product: %v", err) + } + log.Printf("Product ID: %s added successfully", r.Value) + + product, err := c.GetProduct(context.Background(), &pb.ProductID{Value: r.Value}) + if err != nil { + log.Fatalf("Could not get product: %v", err) + } + log.Printf("Product: %v", product.String()) +} diff --git a/src/pb/product-info.pb.go b/src/pb/product-info.pb.go new file mode 100644 index 00000000..3ffb6556 --- /dev/null +++ b/src/pb/product-info.pb.go @@ -0,0 +1,248 @@ +// 版本 + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v5.26.1 +// source: product-info.proto + +// proto文件所属包名 + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// 自定义消息类型,用这种方法传递多个参数,必须使用唯一数字标识每个字段 +type Product struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Price float32 `protobuf:"fixed32,4,opt,name=price,proto3" json:"price,omitempty"` +} + +func (x *Product) Reset() { + *x = Product{} + if protoimpl.UnsafeEnabled { + mi := &file_product_info_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Product) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Product) ProtoMessage() {} + +func (x *Product) ProtoReflect() protoreflect.Message { + mi := &file_product_info_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Product.ProtoReflect.Descriptor instead. +func (*Product) Descriptor() ([]byte, []int) { + return file_product_info_proto_rawDescGZIP(), []int{0} +} + +func (x *Product) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Product) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Product) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Product) GetPrice() float32 { + if x != nil { + return x.Price + } + return 0 +} + +type ProductID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *ProductID) Reset() { + *x = ProductID{} + if protoimpl.UnsafeEnabled { + mi := &file_product_info_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProductID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProductID) ProtoMessage() {} + +func (x *ProductID) ProtoReflect() protoreflect.Message { + mi := &file_product_info_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProductID.ProtoReflect.Descriptor instead. +func (*ProductID) Descriptor() ([]byte, []int) { + return file_product_info_proto_rawDescGZIP(), []int{1} +} + +func (x *ProductID) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +var File_product_info_proto protoreflect.FileDescriptor + +var file_product_info_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x65, 0x0a, 0x07, 0x50, + 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, + 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x70, 0x72, 0x69, + 0x63, 0x65, 0x22, 0x21, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x49, 0x44, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x6d, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x64, 0x64, 0x50, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x12, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x0a, 0x67, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x12, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x49, 0x44, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, + 0x64, 0x75, 0x63, 0x74, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_product_info_proto_rawDescOnce sync.Once + file_product_info_proto_rawDescData = file_product_info_proto_rawDesc +) + +func file_product_info_proto_rawDescGZIP() []byte { + file_product_info_proto_rawDescOnce.Do(func() { + file_product_info_proto_rawDescData = protoimpl.X.CompressGZIP(file_product_info_proto_rawDescData) + }) + return file_product_info_proto_rawDescData +} + +var file_product_info_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_product_info_proto_goTypes = []interface{}{ + (*Product)(nil), // 0: proto.Product + (*ProductID)(nil), // 1: proto.ProductID +} +var file_product_info_proto_depIdxs = []int32{ + 0, // 0: proto.ProductInfo.addProduct:input_type -> proto.Product + 1, // 1: proto.ProductInfo.getProduct:input_type -> proto.ProductID + 1, // 2: proto.ProductInfo.addProduct:output_type -> proto.ProductID + 0, // 3: proto.ProductInfo.getProduct:output_type -> proto.Product + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_product_info_proto_init() } +func file_product_info_proto_init() { + if File_product_info_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_product_info_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Product); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_product_info_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProductID); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_product_info_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_product_info_proto_goTypes, + DependencyIndexes: file_product_info_proto_depIdxs, + MessageInfos: file_product_info_proto_msgTypes, + }.Build() + File_product_info_proto = out.File + file_product_info_proto_rawDesc = nil + file_product_info_proto_goTypes = nil + file_product_info_proto_depIdxs = nil +} diff --git a/src/pb/product-info_grpc.pb.go b/src/pb/product-info_grpc.pb.go new file mode 100644 index 00000000..2178875a --- /dev/null +++ b/src/pb/product-info_grpc.pb.go @@ -0,0 +1,150 @@ +// 版本 + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.26.1 +// source: product-info.proto + +// proto文件所属包名 + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + ProductInfo_AddProduct_FullMethodName = "/proto.ProductInfo/addProduct" + ProductInfo_GetProduct_FullMethodName = "/proto.ProductInfo/getProduct" +) + +// ProductInfoClient is the client API for ProductInfo service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ProductInfoClient interface { + AddProduct(ctx context.Context, in *Product, opts ...grpc.CallOption) (*ProductID, error) + GetProduct(ctx context.Context, in *ProductID, opts ...grpc.CallOption) (*Product, error) +} + +type productInfoClient struct { + cc grpc.ClientConnInterface +} + +func NewProductInfoClient(cc grpc.ClientConnInterface) ProductInfoClient { + return &productInfoClient{cc} +} + +func (c *productInfoClient) AddProduct(ctx context.Context, in *Product, opts ...grpc.CallOption) (*ProductID, error) { + out := new(ProductID) + err := c.cc.Invoke(ctx, ProductInfo_AddProduct_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *productInfoClient) GetProduct(ctx context.Context, in *ProductID, opts ...grpc.CallOption) (*Product, error) { + out := new(Product) + err := c.cc.Invoke(ctx, ProductInfo_GetProduct_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ProductInfoServer is the server API for ProductInfo service. +// All implementations must embed UnimplementedProductInfoServer +// for forward compatibility +type ProductInfoServer interface { + AddProduct(context.Context, *Product) (*ProductID, error) + GetProduct(context.Context, *ProductID) (*Product, error) + mustEmbedUnimplementedProductInfoServer() +} + +// UnimplementedProductInfoServer must be embedded to have forward compatible implementations. +type UnimplementedProductInfoServer struct { +} + +func (UnimplementedProductInfoServer) AddProduct(context.Context, *Product) (*ProductID, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddProduct not implemented") +} +func (UnimplementedProductInfoServer) GetProduct(context.Context, *ProductID) (*Product, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetProduct not implemented") +} +func (UnimplementedProductInfoServer) mustEmbedUnimplementedProductInfoServer() {} + +// UnsafeProductInfoServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ProductInfoServer will +// result in compilation errors. +type UnsafeProductInfoServer interface { + mustEmbedUnimplementedProductInfoServer() +} + +func RegisterProductInfoServer(s grpc.ServiceRegistrar, srv ProductInfoServer) { + s.RegisterService(&ProductInfo_ServiceDesc, srv) +} + +func _ProductInfo_AddProduct_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Product) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProductInfoServer).AddProduct(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProductInfo_AddProduct_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProductInfoServer).AddProduct(ctx, req.(*Product)) + } + return interceptor(ctx, in, info, handler) +} + +func _ProductInfo_GetProduct_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ProductID) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProductInfoServer).GetProduct(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProductInfo_GetProduct_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProductInfoServer).GetProduct(ctx, req.(*ProductID)) + } + return interceptor(ctx, in, info, handler) +} + +// ProductInfo_ServiceDesc is the grpc.ServiceDesc for ProductInfo service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ProductInfo_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "proto.ProductInfo", + HandlerType: (*ProductInfoServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "addProduct", + Handler: _ProductInfo_AddProduct_Handler, + }, + { + MethodName: "getProduct", + Handler: _ProductInfo_GetProduct_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "product-info.proto", +} diff --git a/src/proto/product-info.proto b/src/proto/product-info.proto new file mode 100644 index 00000000..98b89526 --- /dev/null +++ b/src/proto/product-info.proto @@ -0,0 +1,32 @@ +// 版本 +syntax = "proto3"; +// proto文件所属包名 +package proto; +// 声明生成的go文件所属的包名,相对路径是相对于执行编译命令时指定的工作路径 +option go_package = "./proto"; + +// 包含两个远程方法的 rpc 服务,远程方法只能有一个参数和一个返回值 +service ProductInfo { + rpc addProduct(Product) returns (ProductID); + rpc getProduct(ProductID) returns (Product); +} + +// 自定义消息类型,用这种方法传递多个参数,必须使用唯一数字标识每个字段 +message Product { + string id = 1; + string name = 2; + string description = 3; + float price = 4; +} + +message ProductID { + string value = 1; +} + +/* +编译上述proto文件: +protoc \ +--go_out=./src/pb --go_opt=paths=source_relative \ +--go-grpc_out=./src/pb --go-grpc_opt=paths=source_relative \ +--proto_path=/Users/au_miner/opt/module/workspace/rpc/jrpc/src/proto product-info.proto + */ \ No newline at end of file diff --git a/src/server/main.go b/src/server/main.go new file mode 100644 index 00000000..4ba76e11 --- /dev/null +++ b/src/server/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "context" + "log" + "net" + + pb "jrpc/src/pb" + + "github.com/gofrs/uuid" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + port = ":50051" +) + +// 对服务器的抽象,用来实现服务方法 +type server struct { + pb.UnimplementedProductInfoServer +} + +// 存放商品,模拟业务逻辑 +var productMap map[string]*pb.Product + +// 实现 AddProduct 方法 +func (s *server) AddProduct(ctx context.Context, in *pb.Product) (*pb.ProductID, error) { + out, err := uuid.NewV4() + if err != nil { + return nil, status.Errorf(codes.Internal, "Error while generating Product ID", err) + } + in.Id = out.String() + if productMap == nil { + productMap = make(map[string]*pb.Product) + } + productMap[in.Id] = in + log.Printf("Product %v : %v - Added.", in.Id, in.Name) + return &pb.ProductID{Value: in.Id}, nil +} + +// 实现 GetProduct 方法 +func (s *server) GetProduct(ctx context.Context, in *pb.ProductID) (*pb.Product, error) { + product, exists := productMap[in.Value] + if exists && product != nil { + log.Printf("Product %v : %v - Retrieved.", product.Id, product.Name) + return product, nil + } + return nil, status.Errorf(codes.NotFound, "Product does not exist.", in.Value) +} + +func main() { + // 创建一个 tcp 监听器 + lis, err := net.Listen("tcp", port) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + // 创建一个 gRPC 服务器实例 + s := grpc.NewServer() + // 将服务注册到 gRPC 服务器上 + pb.RegisterProductInfoServer(s, &server{}) + // 绑定 gRPC 服务器到指定 tcp + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +}