原文地址:帶入gRPC:gRPC Client and Serverhtml
項目地址:go-grpc-examplegit
本章節將使用 Go 來編寫 gRPC Server 和 Client,讓其互相通信。在此之上會使用到以下庫:github
go get -u google.golang.org/grpc
wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.zip unzip protobuf-all-3.5.1.zip cd protobuf-3.5.1/ ./configure make make install
檢查是否安裝成功golang
protoc --version
若出現如下錯誤,執行 ldconfig
命名就能解決這問題json
protoc: error while loading shared libraries: libprotobuf.so.15: cannot open shared object file: No such file or directory
go get -u github.com/golang/protobuf/protoc-gen-go
安裝環境如有問題,可參考我先前的文章 《介紹與環境安裝》 內有詳細介紹,再也不贅述segmentfault
本小節開始正式編寫 gRPC 相關的程序,一塊兒上車吧 😄tcp
$ tree go-grpc-example go-grpc-example ├── client ├── proto │ └── search.proto └── server.go
在 proto 文件夾下的 search.proto 文件中,寫入以下內容:分佈式
syntax = "proto3"; package proto; service SearchService { rpc Search(SearchRequest) returns (SearchResponse) {} } message SearchRequest { string request = 1; } message SearchResponse { string response = 1; }
在 proto 文件夾下執行以下命令:google
$ protoc --go_out=plugins=grpc:. *.proto
咱們定義的 proto 文件是涉及了 RPC 服務的,而默認是不會生成 RPC 代碼的,所以須要給出 plugins
參數傳遞給 protoc-gen-go
,告訴它,請支持 RPC(這裏指定了 gRPC)spa
該指令會加載 protoc-gen-go 插件達到生成 Go 代碼的目的,生成的文件以 .pb.go 爲文件後綴
冒號充當分隔符的做用,後跟所須要的參數集。若是這處不涉及 RPC,命令可簡化爲:
$ protoc --go_out=. *.proto
注:建議你看看兩條命令生成的 .pb.go 文件,分別有什麼區別
執行完畢命令後,將獲得一個 .pb.go 文件,文件內容以下:
type SearchRequest struct { Request string `protobuf:"bytes,1,opt,name=request" json:"request,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SearchRequest) Reset() { *m = SearchRequest{} } func (m *SearchRequest) String() string { return proto.CompactTextString(m) } func (*SearchRequest) ProtoMessage() {} func (*SearchRequest) Descriptor() ([]byte, []int) { return fileDescriptor_search_8b45f79ee13ff6a3, []int{0} } func (m *SearchRequest) GetRequest() string { if m != nil { return m.Request } return "" }
經過閱讀這一部分代碼,能夠知道主要涉及以下方面:
type SearchRequest struct { Request string `protobuf:"bytes,1,opt,name=request" json:"request,omitempty"` } func (*SearchRequest) Descriptor() ([]byte, []int) { return fileDescriptor_search_8b45f79ee13ff6a3, []int{0} } type SearchResponse struct { Response string `protobuf:"bytes,1,opt,name=response" json:"response,omitempty"` } func (*SearchResponse) Descriptor() ([]byte, []int) { return fileDescriptor_search_8b45f79ee13ff6a3, []int{1} } ... func init() { proto.RegisterFile("search.proto", fileDescriptor_search_8b45f79ee13ff6a3) } var fileDescriptor_search_8b45f79ee13ff6a3 = []byte{ // 131 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x4e, 0x4d, 0x2c, 0x4a, 0xce, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0x9a, 0x5c, 0xbc, 0xc1, 0x60, 0xe1, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x12, 0x21, 0x09, 0x2e, 0xf6, 0x22, 0x08, 0x53, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x55, 0xd2, 0xe1, 0xe2, 0x83, 0x29, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x15, 0x92, 0xe2, 0xe2, 0x28, 0x82, 0xb2, 0xa1, 0x8a, 0xe1, 0x7c, 0x23, 0x0f, 0x98, 0xc1, 0xc1, 0xa9, 0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0xe6, 0x5c, 0x6c, 0x10, 0x01, 0x21, 0x11, 0x88, 0x13, 0xf4, 0x50, 0x2c, 0x96, 0x12, 0x45, 0x13, 0x85, 0x98, 0xa3, 0xc4, 0x90, 0xc4, 0x06, 0x16, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xba, 0x74, 0x95, 0xc0, 0x00, 0x00, 0x00, }
而這一部分代碼主要是圍繞 fileDescriptor
進行,在這裏 fileDescriptor_search_8b45f79ee13ff6a3
表示一個編譯後的 proto 文件,而每個方法都包含 Descriptor 方法,表明着這一個方法在 fileDescriptor
中具體的 Message Field
這一小節將編寫 gRPC Server 的基礎模板,完成一個方法的調用。對 server.go 寫入以下內容:
package main import ( "context" "log" "net" "google.golang.org/grpc" pb "github.com/EDDYCJY/go-grpc-example/proto" ) type SearchService struct{} func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) { return &pb.SearchResponse{Response: r.GetRequest() + " Server"}, nil } const PORT = "9001" func main() { server := grpc.NewServer() pb.RegisterSearchServiceServer(server, &SearchService{}) lis, err := net.Listen("tcp", ":"+PORT) if err != nil { log.Fatalf("net.Listen err: %v", err) } server.Serve(lis) }
接下來編寫 gRPC Go Client 的基礎模板,打開 client/client.go 文件,寫入如下內容:
package main import ( "context" "log" "google.golang.org/grpc" pb "github.com/EDDYCJY/go-grpc-example/proto" ) const PORT = "9001" func main() { conn, err := grpc.Dial(":"+PORT, grpc.WithInsecure()) if err != nil { log.Fatalf("grpc.Dial err: %v", err) } defer conn.Close() client := pb.NewSearchServiceClient(conn) resp, err := client.Search(context.Background(), &pb.SearchRequest{ Request: "gRPC", }) if err != nil { log.Fatalf("client.Search err: %v", err) } log.Printf("resp: %s", resp.GetResponse()) }
$ pwd $GOPATH/github.com/EDDYCJY/go-grpc-example $ go run server.go
$ pwd $GOPATH/github.com/EDDYCJY/go-grpc-example/client $ go run client.go 2018/09/23 11:06:23 resp: gRPC Server
在本章節,咱們對 Protobuf、gRPC Client/Server 分別都進行了介紹。但願你結合文中講述內容再寫一個 Demo 進行深刻了解,確定會更棒 🤔