一、概念
1、gRPC默認使用protocol buffers,這是google開源的一套成熟的結構數據序列化機制(當然也可以使用其他數據格式如JSON),可以用proto files創建gRPC服務,用protocol buffers消息類型來定義方法參數和返回類型。
二、安裝
1、yum install autoconf automake libtool (centos 系統)
2、protocal buffer安裝
從 https://github.com/google/protobuf/releases下載安裝包,解壓,然后操作以下命令:
./autogen.sh ./configure make make install
最后設置環境變量即可:export LD_LIBRARY_PATH=/usr/local/lib
3、安裝 golang protobuf
go get -u github.com/golang/protobuf/proto // golang protobuf 庫 go get -u github.com/golang/protobuf/protoc-gen-go //protoc --go_out 工具
4、安裝 gRPC-go
go get google.golang.org/grpc
無法下載的話去 https://github.com/grpc/grpc-go 下載
三、使用
1、編寫 proto 文件,helloworld.proto
syntax = "proto3"; option objc_class_prefix = "HLW"; package helloworld; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; }
- syntax="proto3":指定protobuf的版本
- package helloworld:聲明一個報名,一般與文件目錄名相同
- import "xxx/xx.proto":導入其他的包,這樣你就可以使用其他的包的數據結構
- required、optional、repeated:表示該字段是否必須填充;required表示必須指定且只能指定一個;當optional表示可選,可指定也可不指定,但不可超過一個不指定值的時候會采用空值,如string類型的字段會用字符串表示;repeated表示可以重復,類似與編程語言中的list
- message Author:在一個message體內定義一個message結構體
- enum:是枚舉類型結構體
- 數字:字段的標識符,不可重復
- 數據類型: int32、int64、uint32、uint64、sint32、sint64、double、float、 string、bool、bytes、enum、message等等
2、使用protoc命令生成相關文件:
protoc --go_out=plugins=grpc:. helloworld.proto ls helloworld.pb.go helloworld.proto
這里用了plugins選項,提供對grpc的支持,否則不會生成Service的接口,方便編寫服務器和客戶端程序
helloworld.pb.go 文件如下:

1 // Code generated by protoc-gen-go. DO NOT EDIT. 2 // source: helloworld.proto 3 4 package helloworld 5 6 import ( 7 context "context" 8 fmt "fmt" 9 proto "github.com/golang/protobuf/proto" 10 grpc "google.golang.org/grpc" 11 math "math" 12 ) 13 14 // Reference imports to suppress errors if they are not otherwise used. 15 var _ = proto.Marshal 16 var _ = fmt.Errorf 17 var _ = math.Inf 18 19 // This is a compile-time assertion to ensure that this generated file 20 // is compatible with the proto package it is being compiled against. 21 // A compilation error at this line likely means your copy of the 22 // proto package needs to be updated. 23 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 24 25 // The request message containing the user's name. 26 type HelloRequest struct { 27 Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 28 XXX_NoUnkeyedLiteral struct{} `json:"-"` 29 XXX_unrecognized []byte `json:"-"` 30 XXX_sizecache int32 `json:"-"` 31 } 32 33 func (m *HelloRequest) Reset() { *m = HelloRequest{} } 34 func (m *HelloRequest) String() string { return proto.CompactTextString(m) } 35 func (*HelloRequest) ProtoMessage() {} 36 func (*HelloRequest) Descriptor() ([]byte, []int) { 37 return fileDescriptor_17b8c58d586b62f2, []int{0} 38 } 39 40 func (m *HelloRequest) XXX_Unmarshal(b []byte) error { 41 return xxx_messageInfo_HelloRequest.Unmarshal(m, b) 42 } 43 func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 44 return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic) 45 } 46 func (m *HelloRequest) XXX_Merge(src proto.Message) { 47 xxx_messageInfo_HelloRequest.Merge(m, src) 48 } 49 func (m *HelloRequest) XXX_Size() int { 50 return xxx_messageInfo_HelloRequest.Size(m) 51 } 52 func (m *HelloRequest) XXX_DiscardUnknown() { 53 xxx_messageInfo_HelloRequest.DiscardUnknown(m) 54 } 55 56 var xxx_messageInfo_HelloRequest proto.InternalMessageInfo 57 58 func (m *HelloRequest) GetName() string { 59 if m != nil { 60 return m.Name 61 } 62 return "" 63 } 64 65 // The response message containing the greetings 66 type HelloReply struct { 67 Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 68 XXX_NoUnkeyedLiteral struct{} `json:"-"` 69 XXX_unrecognized []byte `json:"-"` 70 XXX_sizecache int32 `json:"-"` 71 } 72 73 func (m *HelloReply) Reset() { *m = HelloReply{} } 74 func (m *HelloReply) String() string { return proto.CompactTextString(m) } 75 func (*HelloReply) ProtoMessage() {} 76 func (*HelloReply) Descriptor() ([]byte, []int) { 77 return fileDescriptor_17b8c58d586b62f2, []int{1} 78 } 79 80 func (m *HelloReply) XXX_Unmarshal(b []byte) error { 81 return xxx_messageInfo_HelloReply.Unmarshal(m, b) 82 } 83 func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 84 return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic) 85 } 86 func (m *HelloReply) XXX_Merge(src proto.Message) { 87 xxx_messageInfo_HelloReply.Merge(m, src) 88 } 89 func (m *HelloReply) XXX_Size() int { 90 return xxx_messageInfo_HelloReply.Size(m) 91 } 92 func (m *HelloReply) XXX_DiscardUnknown() { 93 xxx_messageInfo_HelloReply.DiscardUnknown(m) 94 } 95 96 var xxx_messageInfo_HelloReply proto.InternalMessageInfo 97 98 func (m *HelloReply) GetMessage() string { 99 if m != nil { 100 return m.Message 101 } 102 return "" 103 } 104 105 func init() { 106 proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest") 107 proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply") 108 } 109 110 func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_17b8c58d586b62f2) } 111 112 var fileDescriptor_17b8c58d586b62f2 = []byte{ 113 // 149 bytes of a gzipped FileDescriptorProto 114 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9, 115 0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88, 116 0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42, 117 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92, 118 0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71, 119 0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a, 120 0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64, 121 0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x14, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0xb1, 0x2d, 0x62, 0x62, 0xf6, 122 0xf0, 0x09, 0x4f, 0x62, 0x03, 0xbb, 0xd8, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xbe, 0xde, 0x1d, 123 0x2e, 0xc5, 0x00, 0x00, 0x00, 124 } 125 126 // Reference imports to suppress errors if they are not otherwise used. 127 var _ context.Context 128 var _ grpc.ClientConn 129 130 // This is a compile-time assertion to ensure that this generated file 131 // is compatible with the grpc package it is being compiled against. 132 const _ = grpc.SupportPackageIsVersion4 133 134 // GreeterClient is the client API for Greeter service. 135 // 136 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 137 type GreeterClient interface { 138 // Sends a greeting 139 SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) 140 } 141 142 type greeterClient struct { 143 cc *grpc.ClientConn 144 } 145 146 func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { 147 return &greeterClient{cc} 148 } 149 150 func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { 151 out := new(HelloReply) 152 err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...) 153 if err != nil { 154 return nil, err 155 } 156 return out, nil 157 } 158 159 // GreeterServer is the server API for Greeter service. 160 type GreeterServer interface { 161 // Sends a greeting 162 SayHello(context.Context, *HelloRequest) (*HelloReply, error) 163 } 164 165 func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { 166 s.RegisterService(&_Greeter_serviceDesc, srv) 167 } 168 169 func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 170 in := new(HelloRequest) 171 if err := dec(in); err != nil { 172 return nil, err 173 } 174 if interceptor == nil { 175 return srv.(GreeterServer).SayHello(ctx, in) 176 } 177 info := &grpc.UnaryServerInfo{ 178 Server: srv, 179 FullMethod: "/helloworld.Greeter/SayHello", 180 } 181 handler := func(ctx context.Context, req interface{}) (interface{}, error) { 182 return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) 183 } 184 return interceptor(ctx, in, info, handler) 185 } 186 187 var _Greeter_serviceDesc = grpc.ServiceDesc{ 188 ServiceName: "helloworld.Greeter", 189 HandlerType: (*GreeterServer)(nil), 190 Methods: []grpc.MethodDesc{ 191 { 192 MethodName: "SayHello", 193 Handler: _Greeter_SayHello_Handler, 194 }, 195 }, 196 Streams: []grpc.StreamDesc{}, 197 Metadata: "helloworld.proto", 198 }
3、服務器程序
package main import ( "log" "net" pb "test_grpc/helloworld" "google.golang.org/grpc" "golang.org/x/net/context" ) const ( PORT = ":50001" ) type server struct {} func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { log.Println("request: ", in.Name) return &pb.HelloReply{Message: "Hello " + in.Name}, nil } func main() { lis, err := net.Listen("tcp", PORT) if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) log.Println("rpc服務已經開啟") s.Serve(lis) }
4、客戶端程序
package main import ( "log" "os" pb "test_grpc/helloworld" "golang.org/x/net/context" "google.golang.org/grpc" ) const ( address = "localhost:50001" ) func main() { conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewGreeterClient(conn) name := "lin" if len(os.Args) > 1 { name = os.Args[1] } r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Println(r.Message) }