proto 与grpc 详解
protobuf 官方的中文介绍:https://protobuf.com.cn/
标量值类型
一个标量消息字段可以有以下类型之一——该表显示了在 .proto 文件中指定的类型,以及在自动生成的类中对应的类型
Proto 类型 说明 double float int32 使用可变长度编码。对于编码负数效率低下——如果你的字段可能包含负值,请改用 sint32。 int64 使用可变长度编码。对于编码负数效率低下——如果你的字段可能包含负值,请改用 sint64。 uint32 使用可变长度编码。 uint64 使用可变长度编码。 sint32 使用可变长度编码。有符号整数值。这些比常规的 int32 更高效地编码负数。 sint64 使用可变长度编码。有符号整数值。这些比常规的 int64 更高效地编码负数。 fixed32 总是四个字节。如果值经常大于 228,比 uint32 更高效。 fixed64 总是八个字节。如果值经常大于 256,比 uint64 更高效。 sfixed32 总是四个字节。 sfixed64 总是八个字节。 bool string 字符串必须始终包含 UTF-8 编码或 7 位 ASCII 文本,且长度不能超过 232。 bytes 可包含任意字节序列,长度不超过 232。 Proto 类型 C++ 类型 Java/Kotlin 类型[1] Python 类型[3] Go 类型 Ruby 类型 C# 类型 PHP 类型 Dart 类型 Rust 类型 double double double float float64 Float double float double f64 float float float float float32 Float float float double f32 int32 int32_t int int int32 Fixnum 或 Bignum (根据需要) int integer int i32 int64 int64_t long int/long[4] int64 Bignum long integer/string[6] Int64 i64 uint32 uint32_t int[2] int/long[4] uint32 Fixnum 或 Bignum (根据需要) uint integer int u32 uint64 uint64_t long[2] int/long[4] uint64 Bignum ulong integer/string[6] Int64 u64 sint32 int32_t int int int32 Fixnum 或 Bignum (根据需要) int integer int i32 sint64 int64_t long int/long[4] int64 Bignum long integer/string[6] Int64 i64 fixed32 uint32_t int[2] int/long[4] uint32 Fixnum 或 Bignum (根据需要) uint integer int u32 fixed64 uint64_t long[2] int/long[4] uint64 Bignum ulong integer/string[6] Int64 u64 sfixed32 int32_t int int int32 Fixnum 或 Bignum (根据需要) int integer int i32 sfixed64 int64_t long int/long[4] int64 Bignum long integer/string[6] Int64 i64 bool bool boolean bool bool TrueClass/FalseClass bool boolean bool bool string string String str/unicode[5] string String (UTF-8) string string String ProtoString bytes string ByteString str (Python 2), bytes (Python 3) []byte String (ASCII-8BIT) ByteString string List ProtoBytes
字段默认值
当解析消息时,如果编码的消息字节中不包含某个特定字段,那么在解析的对象中访问该字段将返回该字段的默认值。默认值是类型特定的:
- 对于字符串,默认值是空字符串。
- 对于字节,默认值是空字节。
- 对于布尔值,默认值是 false。
- 对于数值类型,默认值是零。
- 对于消息字段,该字段未设置。其确切值取决于语言。详情请参阅生成的代码指南。
- 对于枚举,默认值是第一个定义的枚举值,该值必须为 0。请参阅枚举默认值。
指定包名与路径
syntax = "proto3";
option go_package = "common/stream/proto/v1";//指定生成的文件存放的目录;以及包名
service Greeter {
rpc GetStream (StreamReqDate) returns (stream StreamResData);//服务端流模式
rpc PostStream (stream StreamReqDate) returns (StreamResData);//客户端流模式
rpc AllStream (stream StreamReqDate) returns (stream StreamResData);//双向流模式
}
message StreamReqDate {
string data =1;
}
message StreamResData {
string data =1;
}
PS D:\golang\goproject\src\lnhgo\day05\stream_grpc_test\proto> protoc --go_out=. --go-grpc_out=. stream.proto
生成的目录

查看生成的文件stream_grpc.pb
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.2 // - protoc v7.35.1 // source: stream.proto package v1 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.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( Greeter_GetStream_FullMethodName = "/Greeter/GetStream" Greeter_PostStream_FullMethodName = "/Greeter/PostStream" Greeter_AllStream_FullMethodName = "/Greeter/AllStream" ) // GreeterClient is the client API for Greeter 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 GreeterClient interface { GetStream(ctx context.Context, in *StreamReqDate, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StreamResData], error) PostStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[StreamReqDate, StreamResData], error) AllStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[StreamReqDate, StreamResData], error) } type greeterClient struct { cc grpc.ClientConnInterface } func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { return &greeterClient{cc} } func (c *greeterClient) GetStream(ctx context.Context, in *StreamReqDate, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StreamResData], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[0], Greeter_GetStream_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[StreamReqDate, StreamResData]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_GetStreamClient = grpc.ServerStreamingClient[StreamResData] func (c *greeterClient) PostStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[StreamReqDate, StreamResData], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[1], Greeter_PostStream_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[StreamReqDate, StreamResData]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_PostStreamClient = grpc.ClientStreamingClient[StreamReqDate, StreamResData] func (c *greeterClient) AllStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[StreamReqDate, StreamResData], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[2], Greeter_AllStream_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[StreamReqDate, StreamResData]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_AllStreamClient = grpc.BidiStreamingClient[StreamReqDate, StreamResData] // GreeterServer is the server API for Greeter service. // All implementations must embed UnimplementedGreeterServer // for forward compatibility. type GreeterServer interface { GetStream(*StreamReqDate, grpc.ServerStreamingServer[StreamResData]) error PostStream(grpc.ClientStreamingServer[StreamReqDate, StreamResData]) error AllStream(grpc.BidiStreamingServer[StreamReqDate, StreamResData]) error mustEmbedUnimplementedGreeterServer() } // UnimplementedGreeterServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedGreeterServer struct{} func (UnimplementedGreeterServer) GetStream(*StreamReqDate, grpc.ServerStreamingServer[StreamResData]) error { return status.Error(codes.Unimplemented, "method GetStream not implemented") } func (UnimplementedGreeterServer) PostStream(grpc.ClientStreamingServer[StreamReqDate, StreamResData]) error { return status.Error(codes.Unimplemented, "method PostStream not implemented") } func (UnimplementedGreeterServer) AllStream(grpc.BidiStreamingServer[StreamReqDate, StreamResData]) error { return status.Error(codes.Unimplemented, "method AllStream not implemented") } func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {} func (UnimplementedGreeterServer) testEmbeddedByValue() {} // UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GreeterServer will // result in compilation errors. type UnsafeGreeterServer interface { mustEmbedUnimplementedGreeterServer() } func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) { // If the following call panics, it indicates UnimplementedGreeterServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&Greeter_ServiceDesc, srv) } func _Greeter_GetStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(StreamReqDate) if err := stream.RecvMsg(m); err != nil { return err } return srv.(GreeterServer).GetStream(m, &grpc.GenericServerStream[StreamReqDate, StreamResData]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_GetStreamServer = grpc.ServerStreamingServer[StreamResData] func _Greeter_PostStream_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(GreeterServer).PostStream(&grpc.GenericServerStream[StreamReqDate, StreamResData]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_PostStreamServer = grpc.ClientStreamingServer[StreamReqDate, StreamResData] func _Greeter_AllStream_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(GreeterServer).AllStream(&grpc.GenericServerStream[StreamReqDate, StreamResData]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_AllStreamServer = grpc.BidiStreamingServer[StreamReqDate, StreamResData] // Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Greeter_ServiceDesc = grpc.ServiceDesc{ ServiceName: "Greeter", HandlerType: (*GreeterServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "GetStream", Handler: _Greeter_GetStream_Handler, ServerStreams: true, }, { StreamName: "PostStream", Handler: _Greeter_PostStream_Handler, ClientStreams: true, }, { StreamName: "AllStream", Handler: _Greeter_AllStream_Handler, ServerStreams: true, ClientStreams: true, }, }, Metadata: "stream.proto", }
查看生成的文件stream.pb
// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 // protoc v7.35.1 // source: stream.proto package v1 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) 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 StreamReqDate struct { state protoimpl.MessageState `protogen:"open.v1"` Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StreamReqDate) Reset() { *x = StreamReqDate{} mi := &file_stream_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StreamReqDate) String() string { return protoimpl.X.MessageStringOf(x) } func (*StreamReqDate) ProtoMessage() {} func (x *StreamReqDate) ProtoReflect() protoreflect.Message { mi := &file_stream_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StreamReqDate.ProtoReflect.Descriptor instead. func (*StreamReqDate) Descriptor() ([]byte, []int) { return file_stream_proto_rawDescGZIP(), []int{0} } func (x *StreamReqDate) GetData() string { if x != nil { return x.Data } return "" } type StreamResData struct { state protoimpl.MessageState `protogen:"open.v1"` Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StreamResData) Reset() { *x = StreamResData{} mi := &file_stream_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StreamResData) String() string { return protoimpl.X.MessageStringOf(x) } func (*StreamResData) ProtoMessage() {} func (x *StreamResData) ProtoReflect() protoreflect.Message { mi := &file_stream_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StreamResData.ProtoReflect.Descriptor instead. func (*StreamResData) Descriptor() ([]byte, []int) { return file_stream_proto_rawDescGZIP(), []int{1} } func (x *StreamResData) GetData() string { if x != nil { return x.Data } return "" } var File_stream_proto protoreflect.FileDescriptor const file_stream_proto_rawDesc = "" + "\n" + "\fstream.proto\"#\n" + "\rStreamReqDate\x12\x12\n" + "\x04data\x18\x01 \x01(\tR\x04data\"#\n" + "\rStreamResData\x12\x12\n" + "\x04data\x18\x01 \x01(\tR\x04data2\x99\x01\n" + "\aGreeter\x12-\n" + "\tGetStream\x12\x0e.StreamReqDate\x1a\x0e.StreamResData0\x01\x12.\n" + "\n" + "PostStream\x12\x0e.StreamReqDate\x1a\x0e.StreamResData(\x01\x12/\n" + "\tAllStream\x12\x0e.StreamReqDate\x1a\x0e.StreamResData(\x010\x01B\x18Z\x16common/stream/proto/v1b\x06proto3" var ( file_stream_proto_rawDescOnce sync.Once file_stream_proto_rawDescData []byte ) func file_stream_proto_rawDescGZIP() []byte { file_stream_proto_rawDescOnce.Do(func() { file_stream_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_stream_proto_rawDesc), len(file_stream_proto_rawDesc))) }) return file_stream_proto_rawDescData } var file_stream_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_stream_proto_goTypes = []any{ (*StreamReqDate)(nil), // 0: StreamReqDate (*StreamResData)(nil), // 1: StreamResData } var file_stream_proto_depIdxs = []int32{ 0, // 0: Greeter.GetStream:input_type -> StreamReqDate 0, // 1: Greeter.PostStream:input_type -> StreamReqDate 0, // 2: Greeter.AllStream:input_type -> StreamReqDate 1, // 3: Greeter.GetStream:output_type -> StreamResData 1, // 4: Greeter.PostStream:output_type -> StreamResData 1, // 5: Greeter.AllStream:output_type -> StreamResData 3, // [3:6] is the sub-list for method output_type 0, // [0:3] 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_stream_proto_init() } func file_stream_proto_init() { if File_stream_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_stream_proto_rawDesc), len(file_stream_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_stream_proto_goTypes, DependencyIndexes: file_stream_proto_depIdxs, MessageInfos: file_stream_proto_msgTypes, }.Build() File_stream_proto = out.File file_stream_proto_goTypes = nil file_stream_proto_depIdxs = nil }
生成到上层目录
syntax = "proto3";
option go_package = "../../../common/stream/proto/v1";//指定生成的文件存放的目录;以及包名
service Greeter {
rpc GetStream (StreamReqDate) returns (stream StreamResData);//服务端流模式
rpc PostStream (stream StreamReqDate) returns (StreamResData);//客户端流模式
rpc AllStream (stream StreamReqDate) returns (stream StreamResData);//双向流模式
}
message StreamReqDate {
string data =1;
}
message StreamResData {
string data =1;
}
生成到上级目录需要添加 --unsafe_allow_out_dir_escape参数
PS D:\golang\goproject\src\lnhgo\day05\stream_grpc_test\proto> protoc --go_out=. --go-grpc_out=. stream.proto --unsafe_allow_out_dir_escape
查看生成的目录

查看生成的stream_grpc.pb
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.2 // - protoc v7.35.1 // source: stream.proto package v1 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.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( Greeter_GetStream_FullMethodName = "/Greeter/GetStream" Greeter_PostStream_FullMethodName = "/Greeter/PostStream" Greeter_AllStream_FullMethodName = "/Greeter/AllStream" ) // GreeterClient is the client API for Greeter 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 GreeterClient interface { GetStream(ctx context.Context, in *StreamReqDate, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StreamResData], error) PostStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[StreamReqDate, StreamResData], error) AllStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[StreamReqDate, StreamResData], error) } type greeterClient struct { cc grpc.ClientConnInterface } func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { return &greeterClient{cc} } func (c *greeterClient) GetStream(ctx context.Context, in *StreamReqDate, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StreamResData], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[0], Greeter_GetStream_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[StreamReqDate, StreamResData]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_GetStreamClient = grpc.ServerStreamingClient[StreamResData] func (c *greeterClient) PostStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[StreamReqDate, StreamResData], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[1], Greeter_PostStream_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[StreamReqDate, StreamResData]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_PostStreamClient = grpc.ClientStreamingClient[StreamReqDate, StreamResData] func (c *greeterClient) AllStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[StreamReqDate, StreamResData], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[2], Greeter_AllStream_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[StreamReqDate, StreamResData]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_AllStreamClient = grpc.BidiStreamingClient[StreamReqDate, StreamResData] // GreeterServer is the server API for Greeter service. // All implementations must embed UnimplementedGreeterServer // for forward compatibility. type GreeterServer interface { GetStream(*StreamReqDate, grpc.ServerStreamingServer[StreamResData]) error PostStream(grpc.ClientStreamingServer[StreamReqDate, StreamResData]) error AllStream(grpc.BidiStreamingServer[StreamReqDate, StreamResData]) error mustEmbedUnimplementedGreeterServer() } // UnimplementedGreeterServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedGreeterServer struct{} func (UnimplementedGreeterServer) GetStream(*StreamReqDate, grpc.ServerStreamingServer[StreamResData]) error { return status.Error(codes.Unimplemented, "method GetStream not implemented") } func (UnimplementedGreeterServer) PostStream(grpc.ClientStreamingServer[StreamReqDate, StreamResData]) error { return status.Error(codes.Unimplemented, "method PostStream not implemented") } func (UnimplementedGreeterServer) AllStream(grpc.BidiStreamingServer[StreamReqDate, StreamResData]) error { return status.Error(codes.Unimplemented, "method AllStream not implemented") } func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {} func (UnimplementedGreeterServer) testEmbeddedByValue() {} // UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GreeterServer will // result in compilation errors. type UnsafeGreeterServer interface { mustEmbedUnimplementedGreeterServer() } func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) { // If the following call panics, it indicates UnimplementedGreeterServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&Greeter_ServiceDesc, srv) } func _Greeter_GetStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(StreamReqDate) if err := stream.RecvMsg(m); err != nil { return err } return srv.(GreeterServer).GetStream(m, &grpc.GenericServerStream[StreamReqDate, StreamResData]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_GetStreamServer = grpc.ServerStreamingServer[StreamResData] func _Greeter_PostStream_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(GreeterServer).PostStream(&grpc.GenericServerStream[StreamReqDate, StreamResData]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_PostStreamServer = grpc.ClientStreamingServer[StreamReqDate, StreamResData] func _Greeter_AllStream_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(GreeterServer).AllStream(&grpc.GenericServerStream[StreamReqDate, StreamResData]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type Greeter_AllStreamServer = grpc.BidiStreamingServer[StreamReqDate, StreamResData] // Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Greeter_ServiceDesc = grpc.ServiceDesc{ ServiceName: "Greeter", HandlerType: (*GreeterServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "GetStream", Handler: _Greeter_GetStream_Handler, ServerStreams: true, }, { StreamName: "PostStream", Handler: _Greeter_PostStream_Handler, ClientStreams: true, }, { StreamName: "AllStream", Handler: _Greeter_AllStream_Handler, ServerStreams: true, ClientStreams: true, }, }, Metadata: "stream.proto", }
查看生成的stream.pb文件
// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 // protoc v7.35.1 // source: stream.proto package v1 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) 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 StreamReqDate struct { state protoimpl.MessageState `protogen:"open.v1"` Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StreamReqDate) Reset() { *x = StreamReqDate{} mi := &file_stream_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StreamReqDate) String() string { return protoimpl.X.MessageStringOf(x) } func (*StreamReqDate) ProtoMessage() {} func (x *StreamReqDate) ProtoReflect() protoreflect.Message { mi := &file_stream_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StreamReqDate.ProtoReflect.Descriptor instead. func (*StreamReqDate) Descriptor() ([]byte, []int) { return file_stream_proto_rawDescGZIP(), []int{0} } func (x *StreamReqDate) GetData() string { if x != nil { return x.Data } return "" } type StreamResData struct { state protoimpl.MessageState `protogen:"open.v1"` Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StreamResData) Reset() { *x = StreamResData{} mi := &file_stream_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StreamResData) String() string { return protoimpl.X.MessageStringOf(x) } func (*StreamResData) ProtoMessage() {} func (x *StreamResData) ProtoReflect() protoreflect.Message { mi := &file_stream_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StreamResData.ProtoReflect.Descriptor instead. func (*StreamResData) Descriptor() ([]byte, []int) { return file_stream_proto_rawDescGZIP(), []int{1} } func (x *StreamResData) GetData() string { if x != nil { return x.Data } return "" } var File_stream_proto protoreflect.FileDescriptor const file_stream_proto_rawDesc = "" + "\n" + "\fstream.proto\"#\n" + "\rStreamReqDate\x12\x12\n" + "\x04data\x18\x01 \x01(\tR\x04data\"#\n" + "\rStreamResData\x12\x12\n" + "\x04data\x18\x01 \x01(\tR\x04data2\x99\x01\n" + "\aGreeter\x12-\n" + "\tGetStream\x12\x0e.StreamReqDate\x1a\x0e.StreamResData0\x01\x12.\n" + "\n" + "PostStream\x12\x0e.StreamReqDate\x1a\x0e.StreamResData(\x01\x12/\n" + "\tAllStream\x12\x0e.StreamReqDate\x1a\x0e.StreamResData(\x010\x01B!Z\x1f../../../common/stream/proto/v1b\x06proto3" var ( file_stream_proto_rawDescOnce sync.Once file_stream_proto_rawDescData []byte ) func file_stream_proto_rawDescGZIP() []byte { file_stream_proto_rawDescOnce.Do(func() { file_stream_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_stream_proto_rawDesc), len(file_stream_proto_rawDesc))) }) return file_stream_proto_rawDescData } var file_stream_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_stream_proto_goTypes = []any{ (*StreamReqDate)(nil), // 0: StreamReqDate (*StreamResData)(nil), // 1: StreamResData } var file_stream_proto_depIdxs = []int32{ 0, // 0: Greeter.GetStream:input_type -> StreamReqDate 0, // 1: Greeter.PostStream:input_type -> StreamReqDate 0, // 2: Greeter.AllStream:input_type -> StreamReqDate 1, // 3: Greeter.GetStream:output_type -> StreamResData 1, // 4: Greeter.PostStream:output_type -> StreamResData 1, // 5: Greeter.AllStream:output_type -> StreamResData 3, // [3:6] is the sub-list for method output_type 0, // [0:3] 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_stream_proto_init() } func file_stream_proto_init() { if File_stream_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_stream_proto_rawDesc), len(file_stream_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_stream_proto_goTypes, DependencyIndexes: file_stream_proto_depIdxs, MessageInfos: file_stream_proto_msgTypes, }.Build() File_stream_proto = out.File file_stream_proto_goTypes = nil file_stream_proto_depIdxs = nil }
go 控制grpc的metadata
gRPC让我们可以像本地调用一样实现远程调用,对于每一次的RPC调用中,都可能会有一些有用的数据,而这些数据就可以通过metadata来传递。metadata是以key-value的形式存储数据的,其中key是string类型,而value是[]string,即一个字符串数组类型。metadata使得client和server能够为对方提供关于本次调用的一些信息,就像一次http请求的RequestHeader和ResponseHeader一样。http中header的生命周周期是一次http请求,那么metadata的生命周期就是一次RPC调用。
新建matedata
MD 类型实际上是map,key是string,value是string类型的slice。
type MD map[string][]string
创建的时候可以像创建普通的map类型一样使用new关键字进行创建:
//第一种方式
md := metadata.New(map[string]string{"key1": "val1", "key2": "val2"})
//第二种方式 key不区分大小写,会被统一转成小写。
md := metadata.Pairs(
"key1", "val1",
"key1", "val1-2", // "key1" will have map value []string{"val1", "val1-2"}
"key2", "val2",
)
2发送
md := metadata.Pairs("key", "val")
// 新建一个有 metadata 的 context
ctx := metadata.NewOutgoingContext(context.Background(), md)
// 单向 RPC
response, err := client.SomeRPC(ctx, someRequest)
3.接收
func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {
md, ok := metadata.FromIncomingContext(ctx)
// do something with metadata
}
proto文件引入另一个proto文件
hello.proto 文件;注意大小写一致
syntax = "proto3";
import "base.proto";
import "google/protobuf/empty.proto";
option go_package = ".;proto";
service Greeter {
rpc SayHello(HelloRequest) returns(HelloReply);
rpc Ping(Empty) returns(Pong);
}
message HelloRequest {
string url = 1;
string name = 2;
}
message HelloReply {
string message =1;
//嵌套
message Result {
string url=1;
string name =2;
}
repeated Result data =2;
}
base.proto 文件注意包名
syntax = "proto3";
option go_package = ".;proto";//需要指定包,否则不知道哪个包下的
message Empty {
}
message Pong {
string id =1;
}
PS D:\golang\goproject\src\lnhgo> cd protobuf\day3\grpc_proto_test\proto
PS D:\golang\goproject\src\lnhgo\protobuf\day3\grpc_proto_test\proto> protoc --go_out=. --go-grpc_out=. Hello.proto
引入互联网公共文件与本地文件
syntax = "proto3";
option go_package = ".;proto";
import "base.proto";//本地文件
import "google/protobuf/empty.proto";//公共文件
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
rpc Ping (google.protobuf.Empty) returns (Pong);
}
message HelloRequest {
string url =1;
string name =2;
}
message HelloReply {
string message =1;
}
base.protu文件
syntax = "proto3";
option go_package = ".;proto";//需要指定包,否则不知道哪个包下的
message Empty {
}
message Pong {
string id =1;
}
执行命令
PS D:\golang\goproject\src\lnhgo\protobuf\day3\grpc_proto_test\proto> protoc --go_out=. --go-grpc_out=. Hello.proto base.proto
嵌套message 的实现
syntax = "proto3";
option go_package = ".;proto";
import "base.proto";//本地文件
import "google/protobuf/empty.proto";//公共文件
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
rpc Ping (google.protobuf.Empty) returns (Pong);
}
message HelloRequest {
string url =1;
string name =2;
}
message HelloReply {
string message =1;
//嵌套
message Result {
string name=1;
string url =2;
}
repeated Result data=2;
}
PS D:\golang\goproject\src\lnhgo\protobuf\day3\grpc_proto_test\proto> protoc --go_out=. --go-grpc_out=. Hello.proto base.proto
嵌套对象的使用
package main
import (
"lnhgo/protobuf/day3/grpc_proto_test/proto"
)
func main(){
proto.HelloReply_Result{}//嵌套对象的使用
proto.Pong{}
}
枚举类型
syntax = "proto3";
import "base.proto";
import "google/protobuf/empty.proto";
option go_package = ".;proto";
service Greeter {
rpc SayHello(HelloRequest) returns(HelloReply);
rpc Ping(empty) returns(Pong);
}
enum Gender {// 枚举类型
MALE =1;
FEMALE =2;
}
message HelloRequest {
string url = 1;
string name = 2;
Gender g =3;//枚举类型
}
message HelloReply {
string message =1;
}
map 类型
syntax = "proto3";
import "base.proto";
//import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
option go_package = ".;proto";
service Greeter {
rpc SayHello(HelloRequest) returns(HelloReply);
rpc Ping(Empty) returns(Pong);
}
enum Gender {// 枚举类型
MALE =0;
FEMALE =1;
}
message HelloRequest {
string url = 1;
string name = 2;
Gender g =3;//枚举类型
map<string,string> mp=4; //map
google.protobuf.Timestamp addTime =5;
}
message HelloReply {
string message =1;
}
时间戳
syntax = "proto3";
import "base.proto";
//import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
option go_package = ".;proto";
service Greeter {
rpc SayHello(HelloRequest) returns(HelloReply);
rpc Ping(Empty) returns(Pong);
}
enum Gender {// 枚举类型
MALE =0;
FEMALE =1;
}
message HelloRequest {
string url = 1;
string name = 2;
Gender g =3;//枚举类型
map<string,string> mp=4;
google.protobuf.Timestamp addTime =5;//时间戳
}
message HelloReply {
string message =1;
}
go控制grpc的metadata
gRPC让我们可以像本地调用一样实现远程调用,对于每一次的RPC调用中,都可能会有一些有用的数据,而这些数据就可以通过metadata来传递。metadata是以key-value的形式存储数据的,其中key是string类型,而value是[]string,即一个字符串数组类型。metadata使得client和server能够为对方提供关于本次调用的一些信息,就像一次http请求的RequestHeader和ResponseHeader一样。http中header的生命周周期是一次http请求,那么metadata的生命周期就是一次RPC调用。
示例
client
package main
import (
"context"
"fmt"
"lnhgo/metadata_test/proto"
//"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
func main() {
conn, err := grpc.NewClient(
"127.0.0.1:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err !=nil{
panic("连接失败")
}
defer conn.Close()
c:=proto.NewGreeterClient(conn)
//timestampFormat := ""
//md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))
md := metadata.New(map[string]string{
"name":"bobby",
"pasword":"imooc",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
//r,err:=c.SayHello(context.Background(),&proto.HelloRequest{Name: "chenxi"})
//r,err:=c.SayHello(context.Background(),&proto.HelloRequest{})
r,err:=c.SayHello(ctx,&proto.HelloRequest{Name: "bobby"})
if err != nil{
panic("调用失败")
}
fmt.Println(r.Message)
}
server
package main
import (
"context"
"fmt"
"lnhgo/metadata_test/proto"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
type Server struct{
proto.UnimplementedGreeterServer
}
func (s *Server) SayHello(c context.Context,request *proto.HelloRequest)(*proto.HelloReply,error){
//matadata := nil
md,ok:=metadata.FromIncomingContext(c)
if ok {
fmt.Println("get metadata error")
}
for key,val:=range md{
fmt.Println(key,val)
}
if name,ok:=md["name"];ok{
fmt.Println(name)
}
return &proto.HelloReply{
Message: "hello "+request.Name,
},nil
}
//func (s *Server)mustEmbedUnimplementedGreeterServer(){}
func main(){
g:=grpc.NewServer()
proto.RegisterGreeterServer(g,&Server{})
lis,err:=net.Listen("tcp","0.0.0.0:8080")
if err !=nil {
panic("监听失败")
}
err=g.Serve(lis)
if err !=nil {
panic("启动失败")
}
}
执行结果
服务端启动 PS D:\golang\goproject\src\lnhgo> go run protobuf\day2\server\server.go 客户端启动 PS D:\golang\goproject\src\lnhgo> go run protobuf\day2\client\client.go hello bobby 服务终端显示 get metadata error :authority [127.0.0.1:8080] content-type [application/grpc] user-agent [grpc-go/1.82.1] name [bobby] pasword [imooc] [bobby]
grpc 拦截器
server 端拦截器
package main
import (
"context"
"fmt"
"lnhgo/grpc_interpretor/proto"
"net"
"google.golang.org/grpc"
//"google.golang.org/grpc/metadata"
)
type Server struct{
proto.UnimplementedGreeterServer
}
func (s *Server) SayHello(c context.Context,request *proto.HelloRequest)(*proto.HelloReply,error){
//matadata := nil
// md,ok:=metadata.FromIncomingContext(c)
// if ok {
// fmt.Println("get metadata error")
// }
// for key,val:=range md{
// fmt.Println(key,val)
// }
// if name,ok:=md["name"];ok{
// fmt.Println(name)
// }
return &proto.HelloReply{
Message: "hello "+request.Name,
},nil
}
//func (s *Server)mustEmbedUnimplementedGreeterServer(){}
func main(){
interceptor:=func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error){
fmt.Println("接收到请求")
res,err:= handler(ctx,req)
fmt.Println("请求完成")
return res ,err
}
opt:= grpc.UnaryInterceptor(interceptor)//server 拦截器
g:=grpc.NewServer(opt)
proto.RegisterGreeterServer(g,&Server{})
lis,err:=net.Listen("tcp","0.0.0.0:8080")
if err !=nil {
panic("监听失败")
}
err=g.Serve(lis)
if err !=nil {
panic("启动失败")
}
}
PS D:\golang\goproject\src\lnhgo> go run grpc_interpretor\server\server.go
测试代码
package main
import (
"context"
"fmt"
"lnhgo/metadata_test/proto"
//"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
func main() {
conn, err := grpc.NewClient(
"127.0.0.1:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err !=nil{
panic("连接失败")
}
defer conn.Close()
c:=proto.NewGreeterClient(conn)
//timestampFormat := ""
//md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))
md := metadata.New(map[string]string{
"name":"bobby",
"pasword":"imooc",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
//r,err:=c.SayHello(context.Background(),&proto.HelloRequest{Name: "chenxi"})
//r,err:=c.SayHello(context.Background(),&proto.HelloRequest{})
r,err:=c.SayHello(ctx,&proto.HelloRequest{Name: "bobby"})
if err != nil{
panic("调用失败")
}
fmt.Println(r.Message)
}
PS D:\golang\goproject\src\lnhgo> go run metadata_test\client\client.go
hello bobby
服务端显示
接收到请求 请求完成
client 拦截器(下面代码运行测试用例是,服务端处于运行状态server代码跟上面一致)
package main
import (
"context"
"fmt"
"lnhgo/metadata_test/proto"
"time"
//"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
func main() {
interceptor:=func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error{
fmt.Println("==========进入客户端拦截器==========")
fmt.Println("method:", method)
start:= time.Now()
err:=invoker(ctx,method,req,reply,cc,opts...)
fmt.Printf("耗时:%s\n",time.Since(start))
return err
}
ops:=grpc.WithUnaryInterceptor(interceptor)
conn, err := grpc.NewClient(
"127.0.0.1:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
ops,
)
if err !=nil{
panic("连接失败")
}
defer conn.Close()
c:=proto.NewGreeterClient(conn)
md := metadata.New(map[string]string{
"name":"bobby",
"pasword":"imooc",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
r,err:=c.SayHello(ctx,&proto.HelloRequest{Name: "bobby"})
if err != nil{
panic("调用失败")
}
fmt.Println(r.Message)
}
测试
PS D:\golang\goproject\src\lnhgo> go run grpc_interpretor\client\client.go
==========进入客户端拦截器==========
method: /Greeter/SayHello
耗时:2.8415ms
hello bobby
通过metadata实现grpc的认证
client
package main
import (
"context"
"fmt"
"lnhgo/grpc_token_auth_test/proto"
"time"
//"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
func main() {
interceptor:=func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error{
fmt.Println("==========进入客户端拦截器==========")
fmt.Println("method:", method)
start:= time.Now()
md:= metadata.New(map[string]string{
"appid":"101010",
"appkey":"i am key",
})
ctx=metadata.NewOutgoingContext(context.Background(),md)
err:=invoker(ctx,method,req,reply,cc,opts...)
fmt.Printf("耗时:%s\n",time.Since(start))
return err
}
ops:=grpc.WithUnaryInterceptor(interceptor)
conn, err := grpc.NewClient(
"127.0.0.1:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
ops,
)
if err !=nil{
panic("连接失败")
}
defer conn.Close()
c:=proto.NewGreeterClient(conn)
md := metadata.New(map[string]string{
"name":"bobby",
"pasword":"imooc",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
r,err:=c.SayHello(ctx,&proto.HelloRequest{Name: "bobby"})
if err != nil{
panic("调用失败")
}
fmt.Println(r.Message)
}
server
package main
import (
"context"
"fmt"
"lnhgo/grpc_token_auth_test/proto"
"net"
//"google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"//如果没有status 请执行下载命令go get google.golang.org/grpc 和 go get google.golang.org/grpc/status
)
type Server struct{
proto.UnimplementedGreeterServer
}
func (s *Server) SayHello(c context.Context,request *proto.HelloRequest)(*proto.HelloReply,error){
//matadata := nil
// md,ok:=metadata.FromIncomingContext(c)
// if ok {
// fmt.Println("get metadata error")
// }
// for key,val:=range md{
// fmt.Println(key,val)
// }
// if name,ok:=md["name"];ok{
// fmt.Println(name)
// }
return &proto.HelloReply{
Message: "hello "+request.Name,
},nil
}
//func (s *Server)mustEmbedUnimplementedGreeterServer(){}
func main(){
interceptor:=func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error){
fmt.Println("接收到请求")
md,ok:=metadata.FromIncomingContext(ctx)
fmt.Println(md)
if !ok {
return resp,status.Error(codes.Unauthenticated,"无token认证信息")
//fmt.Println("get metadata error")
}
var (
appid string
appkey string
)
if va1,ok:=md["appid"];ok {
appid = va1[0]
fmt.Println(appid)
}
if va2,ok:=md["appkey"];ok {
appkey = va2[0]
fmt.Println(appkey)
}
if appid != "101010"|| appkey != "i am key"{
fmt.Println("验证失败")
return resp,status.Error(codes.Unauthenticated,"认证信息错误")
}
// if nameSlice,ok:=md["appid"];ok{
// fmt.Println(nameSlice)
// for i,e:=range nameSlice{
// fmt.Println(i,e)
// }
// }
res,err:= handler(ctx,req)
fmt.Println("请求完成")
return res ,err
}
opt:= grpc.UnaryInterceptor(interceptor)//server 拦截器
g:=grpc.NewServer(opt)
proto.RegisterGreeterServer(g,&Server{})
lis,err:=net.Listen("tcp","0.0.0.0:8080")
if err !=nil {
panic("监听失败")
}
err=g.Serve(lis)
if err !=nil {
panic("启动失败")
}
}
启动
server 启动 PS D:\golang\goproject\src\lnhgo> go run grpc_token_auth_test\server\server.go client 测试启动 PS D:\golang\goproject\src\lnhgo> go run grpc_token_auth_test\client\client.go ==========进入客户端拦截器========== method: /Greeter/SayHello 耗时:3.1413ms hello bobby 观察server 的输出 接收到请求 map[:authority:[127.0.0.1:8080] appid:[101010] appkey:[i am key] content-type:[application/grpc] user-agent:[grpc-go/1.83.2]] 101010 i am key 请求完成
客户端传递认证方式2
package main
import (
"context"
"fmt"
"lnhgo/grpc_token_auth_test/proto"
//"time"
//"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
type customCredential struct{}
func (c customCredential)GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error){
return map[string]string{
"appid":"101010",
"appkey":"i am key",
},nil
}
func (c customCredential)RequireTransportSecurity() bool{
return false
}
func main() {
// interceptor:=func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error{
// fmt.Println("==========进入客户端拦截器==========")
// fmt.Println("method:", method)
// start:= time.Now()
// md:= metadata.New(map[string]string{
// "appid":"101010",
// "appkey":"i am key",
// })
// ctx=metadata.NewOutgoingContext(context.Background(),md)
// err:=invoker(ctx,method,req,reply,cc,opts...)
// fmt.Printf("耗时:%s\n",time.Since(start))
// return err
// }
// ops:=grpc.WithUnaryInterceptor(interceptor)
conn, err := grpc.NewClient(
"127.0.0.1:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
//ops,
grpc.WithPerRPCCredentials(customCredential{}),
)
if err !=nil{
panic("连接失败")
}
defer conn.Close()
c:=proto.NewGreeterClient(conn)
md := metadata.New(map[string]string{
"name":"bobby",
"pasword":"imooc",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
r,err:=c.SayHello(ctx,&proto.HelloRequest{Name: "bobby"})
if err != nil{
panic("调用失败")
}
fmt.Println(r.Message)
}
测试
PS D:\golang\goproject\src\lnhgo> go run grpc_token_auth_test\client\client.go
hello bobby
客户端传递认证方式3+ 拦截
package main
import (
"context"
"fmt"
"lnhgo/grpc_token_auth_test/proto"
"time"
//"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
type customCredential struct{}
func (c customCredential)GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error){
return map[string]string{
"appid":"101010",
"appkey":"i am key",
},nil
}
func (c customCredential)RequireTransportSecurity() bool{
return false
}
func main() {
interceptor:=func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error{
fmt.Println("==========进入客户端拦截器==========")
fmt.Println("method:", method)
start:= time.Now()
md:= metadata.New(map[string]string{
"appid":"101010",
"appkey":"i am key",
})
ctx=metadata.NewOutgoingContext(context.Background(),md)
err:=invoker(ctx,method,req,reply,cc,opts...)
fmt.Printf("耗时:%s\n",time.Since(start))
return err
}
ops:=grpc.WithUnaryInterceptor(interceptor)
conn, err := grpc.NewClient(
"127.0.0.1:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
ops,
grpc.WithPerRPCCredentials(customCredential{}),
)
if err !=nil{
panic("连接失败")
}
defer conn.Close()
c:=proto.NewGreeterClient(conn)
md := metadata.New(map[string]string{
"name":"bobby",
"pasword":"imooc",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
r,err:=c.SayHello(ctx,&proto.HelloRequest{Name: "bobby"})
if err != nil{
panic("调用失败")
}
fmt.Println(r.Message)
}
执行结果
PS D:\golang\goproject\src\lnhgo> go run grpc_token_auth_test\client\client.go
==========进入客户端拦截器==========
method: /Greeter/SayHello
耗时:2.9185ms
hello bobby
grpc 的验证器
Windows安装插件
D:\goproject\bin>go install github.com/envoyproxy/protoc-gen-validate@latest go: downloading github.com/envoyproxy/protoc-gen-validate v1.3.3 go: downloading github.com/lyft/protoc-gen-star/v2 v2.0.4 go: downloading github.com/iancoleman/strcase v0.3.0 go: downloading golang.org/x/tools v0.41.0 go: downloading github.com/spf13/afero v1.15.0 go: downloading golang.org/x/text v0.33.0 go: downloading golang.org/x/sync v0.19.0 go: downloading golang.org/x/mod v0.32.0
1.新建validate.proto文件内容从此处拷贝
2.准备proto文件
syntax = "proto3";
import "validate.proto";
option go_package=".;proto";
service Greeter {
rpc SayHello (Person) returns (Person);
}
message Person {
uint64 id = 1 [(validate.rules).uint64.gt = 999];
string email = 2 [(validate.rules).string.email = true];
string mobile = 3 [(validate.rules).string = {
pattern: "^1[3456789]\\d{9}$"}];
}
下载依赖
PS D:\golang\goproject\src\lnhgo\grpc_validate_test\proto> go get github.com/envoyproxy/protoc-gen-validate@latest go: downloading golang.org/x/tools v0.48.0 go: downloading golang.org/x/mod v0.38.0 go: downloading golang.org/x/sync v0.22.0 go: added github.com/iancoleman/strcase v0.3.0 go: added github.com/lyft/protoc-gen-star/v2 v2.0.4 go: added github.com/spf13/afero v1.15.0
生成文件
PS D:\golang\goproject\src\lnhgo\grpc_validate_test\proto> protoc -I . --go_out=. --go-grpc_out=. --validate_out="lang=go:." helloworld.proto
server
package main
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"net"
"google.golang.org/grpc"
"lnhgo/grpc_validate_test/proto"
)
type Server struct{
proto.UnimplementedGreeterServer
}
//func (s *Server) mustEmbedUnimplementedGreeterServer(){}
func (s *Server) SayHello(ctx context.Context, request *proto.Person) (*proto.Person,
error){
return &proto.Person{
Id: 32,
}, nil
}
type Validator interface {
Validate() error
}
func main(){
// p:=new(proto.Person)
// p.Id=1000
// err:=p.Validate()
// if err != nil{
// panic(err)
// }
var interceptor grpc.UnaryServerInterceptor
interceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
// 继续处理请求
if r, ok := req.(Validator); ok {
if err := r.Validate(); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
}
return handler(ctx, req)
}
var opts []grpc.ServerOption
opts = append(opts, grpc.UnaryInterceptor(interceptor))
g := grpc.NewServer(opts...)
proto.RegisterGreeterServer(g, &Server{})
lis, err := net.Listen("tcp", "0.0.0.0:50051")
if err != nil{
panic("failed to listen:"+err.Error())
}
err = g.Serve(lis)
if err != nil{
panic("failed to start grpc:"+err.Error())
}
}
启动server
PS D:\golang\goproject\src\lnhgo> go run grpc_validate_test\server\server.go
client文件
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"lnhgo/grpc_validate_test/proto"
)
type customCredential struct{}
func main() {
var opts []grpc.DialOption
//opts = append(opts, grpc.WithUnaryInterceptor(interceptor))
opts = append(opts, grpc.WithInsecure())
conn, err := grpc.Dial("localhost:50051", opts...)
if err != nil {
panic(err)
}
defer conn.Close()
c := proto.NewGreeterClient(conn)
//rsp, _ := c.Search(context.Background(), &empty.Empty{})
rsp, err := c.SayHello(context.Background(), &proto.Person{
Id: 1000,
Email: "bobby@imocc.com",
Mobile: "18888888843",
})
if err != nil {
panic(err)
}
fmt.Println(rsp.Id)
}
客户端启动
PS D:\golang\goproject\src\lnhgo> go run grpc_validate_test\client\client.go 32
grpc 的异常处理
grpc 的状态码
go 的异常处理
server
package main
import (
"context"
"lnhgo/grpc_error_test/proto"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type Server struct{
proto.UnimplementedGreeterServer
}
func (s *Server) SayHello(c context.Context,request *proto.HelloRequest)(*proto.HelloReply,error){
// return &proto.HelloReply{
// Message: "hello "+request.Name,
// },nil
return nil,status.Errorf(codes.NotFound,"记录未找到%s",request.Name)//错误处理
}
//func (s *Server)mustEmbedUnimplementedGreeterServer(){}
func main(){
g:=grpc.NewServer()
proto.RegisterGreeterServer(g,&Server{})
lis,err:=net.Listen("tcp","0.0.0.0:8080")
if err !=nil {
panic("监听失败")
}
err=g.Serve(lis)
if err !=nil {
panic("启动失败")
}
}
启动
PS D:\golang\goproject\src\lnhgo> go run grpc_error_test\server\server.go
client 文件
package main
import (
"context"
"fmt"
"lnhgo/grpc_error_test/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
)
func main() {
conn, err := grpc.NewClient(
"127.0.0.1:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err !=nil{
panic("连接失败")
}
defer conn.Close()
c:=proto.NewGreeterClient(conn)
_,err=c.SayHello(context.Background(),&proto.HelloRequest{Name: "chenxi"})
if err != nil{
st, ok := status.FromError(err)
if !ok {
// Error was not a status error
panic("解析error 错误")
}
fmt.Println(st.Message())
fmt.Println(st.Code())
// panic(err)
}
//fmt.Println(r.Message)
}
执行结果
PS D:\golang\goproject\src\lnhgo> go run grpc_error_test\client\client.go
记录未找到chenxi
NotFound
grpc 的超时机制
server
package main
import (
"context"
"lnhgo/grpc_error_test/proto"
"net"
"time"
"google.golang.org/grpc"
//"google.golang.org/grpc/codes"
//"google.golang.org/grpc/status"
)
type Server struct{
proto.UnimplementedGreeterServer
}
func (s *Server) SayHello(c context.Context,request *proto.HelloRequest)(*proto.HelloReply,error){
time.Sleep(5*time.Second) // 休眠5秒
return &proto.HelloReply{
Message: "hello "+request.Name,
},nil
//return nil,status.Errorf(codes.NotFound,"记录未找到%s",request.Name)//错误处理
}
//func (s *Server)mustEmbedUnimplementedGreeterServer(){}
func main(){
g:=grpc.NewServer()
proto.RegisterGreeterServer(g,&Server{})
lis,err:=net.Listen("tcp","0.0.0.0:8080")
if err !=nil {
panic("监听失败")
}
err=g.Serve(lis)
if err !=nil {
panic("启动失败")
}
}
启动
PS D:\golang\goproject\src\lnhgo> go run grpc_error_test\server\server.go
client
package main
import (
"context"
"fmt"
"lnhgo/grpc_error_test/proto"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
)
func main() {
conn, err := grpc.NewClient(
"127.0.0.1:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err !=nil{
panic("连接失败")
}
defer conn.Close()
c:=proto.NewGreeterClient(conn)
ctx,_:= context.WithTimeout(context.Background(),time.Second*3) //设置调用超时时间为3秒
_,err=c.SayHello(ctx,&proto.HelloRequest{Name: "chenxi"})
if err != nil{
st, ok := status.FromError(err)
if !ok {
// Error was not a status error
panic("解析error 错误")
}
fmt.Println(st.Message())
fmt.Println(st.Code())
// panic(err)
}
//fmt.Println(r.Message)
}
启动
PS D:\golang\goproject\src\lnhgo> go run grpc_error_test\client\client.go
context deadline exceeded
DeadlineExceeded

浙公网安备 33010602011771号