你们好,我是煎鱼,本章节将使用 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,对做者是一种鼓励和推动。