你們好,我是煎魚,本章節將使用 Go 來編寫 gRPC Server 和 Client,讓其互相通信。在此之上會使用到以下庫:git
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
複製代碼
檢查是否安裝成功github
protoc --version
複製代碼
若出現如下錯誤,執行 ldconfig
命名就能解決這問題golang
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
複製代碼
安裝環境如有問題,可參考我先前的文章 《介紹與環境安裝》 內有詳細介紹,再也不贅述json
本小節開始正式編寫 gRPC 相關的程序,一塊兒上車吧 😄segmentfault
$ tree go-grpc-example
go-grpc-example
├── client
├── proto
│ └── search.proto
└── server.go
複製代碼
在 proto 文件夾下的 search.proto 文件中,寫入以下內容:bash
syntax = "proto3";
package proto;
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse) {}
}
message SearchRequest {
string request = 1;
}
message SearchResponse {
string response = 1;
}
複製代碼
在 proto 文件夾下執行以下命令:tcp
$ protoc --go_out=plugins=grpc:. *.proto
複製代碼
咱們定義的 proto 文件是涉及了 RPC 服務的,而默認是不會生成 RPC 代碼的,所以須要給出 plugins
參數傳遞給 protoc-gen-go
,告訴它,請支持 RPC(這裏指定了 gRPC)ui
該指令會加載 protoc-gen-go 插件達到生成 Go 代碼的目的,生成的文件以 .pb.go 爲文件後綴google
冒號充當分隔符的做用,後跟所須要的參數集。若是這處不涉及 RPC,命令可簡化爲:spa
$ 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 進行深刻了解,確定會更棒 🤔
若是有任何疑問或錯誤,歡迎在 issues 進行提問或給予修正意見,若是喜歡或對你有所幫助,歡迎 Star,對做者是一種鼓勵和推動。