Fork me on GitHub

gRPC

gRPC 体验

gRPC

gRPC 是什么呢?它起源于 RPC(Remote Procedure Call),远程过程调用。它帮助应用开发者从远程服务端直接获取服务而无需关心底层架构。官方介绍:

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。

综合来说,GRPC 是 Google 基于 RPC 而开发的通用 RPC 框架。为何会自称通用呢?因为它可以支持跨语言的客户端和服务端交互。

gRPC

这是一个十分具有诱惑力的功能,它的存在和 Docker 一样,解决的就是一个”交叉”问题。那么它如何解决这个问题呢?答案就是 Protocol Buffer

Protocol Buffer

Protocol Buffer 是 Google 开源的一套成熟的结构数据序列化机制。在 gRPC 中,使用 proto files 创建 gRPC 服务,用 protocol buffers 消息类型来定义方法参数和返回类型。更多的资料参阅 Protocol Buffers 的官方文档

开始使用 gRPC

安装 gRPC 和 protocol buffers

通过以下两条 Go 命令,我们可以把 Go语言版本的 protocol buffers 和 gRPC 下载到本地。

1
2
go get -u google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-go

当然,go get 是很多问题的(对,我就是那个有问题的),那么我们”曲线救国”:

1
2
3
4
5
6
7
8
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
go get -u github.com/golang/protobuf/protoc-gen-go
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

cd $GOPATH/src/
go install google.golang.org/grpc

运行 HelloWorld

这个代码就在下载的 gRPC 中,我们先进入 examples 目录。

1
cd $GOPATH/src/google.golang.org/grpc/examples
  1. 启动 Server
1
$ go run greeter_server/main.go
  1. 启动 Client

另启终端:

1
$ go run greeter_client/main.go

这时,就能在客户端看到 Hello World了。服务端也有了访问记录。

HelloWorld

阅读源码

gRPC 的代码,更新 proto 文件,然后实现其中的接口。我们来看看它里面的代码。

  1. proto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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;
}

通俗易懂,它定义了一个 service,当收到 HelloRequest 的时候,回复一个 HelloReply。

  1. service
1
2
3
4
5
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.Name)
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

在 service 的 main.go 中,实现 SayHello 接口,这样就完成了接口的实现了。

再次 Hello

那么我们来改改代码,熟悉一下。

  1. proto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
// Sends another greeting
rpc SayHelloAgain (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;
}

我们增加了一个重新 helloworld 的接口。重新生成一下 proto 代码:

1
protoc -I helloworld/ helloworld/helloworld.proto --go_out=plugins=grpc:helloworld

  1. service

新增了接口,我们要完成接口的定义:

1
2
3
func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello again " + in.Name}, nil
}
  1. client

在服务端调用一下,测试新接口:

1
2
3
4
5
r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
  1. 测试

全部完成,重新运行服务端和客户端。我们可以看到,客户端会输出:

1
2
Greeting: Hello world
Greeting: Hello again world

总结

这个只是 gRPC 的一个小例子。gRPC 还是很强大的。日后有需要,还要进一步学习。