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

image.png

一、快速入门

根据官方入门指南 , 使用proto文件自动生成 hello_grpc_grpc_pb.go !

  1. go get google.golang.org/grpc
  2. $ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
  3. $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
  4. # 可以写到文件里,执行./build.bat
  5. $ protoc --go_out=. --go_opt=paths=source_relative \
  6. --go-grpc_out=. --go-grpc_opt=paths=source_relative \
  7. ./hello_grpc.proto
  8. # 报错 : 无法确定导入路径,加一行即可
  9. option go_package="./;golang";

以下是hello_grpc.proto

  1. syntax = "proto3";
  2. option go_package="./;golang";
  3. package hello_grpc;
  4. message Request {
  5. string message = 1;
  6. }
  7. message Response {
  8. string message = 1;
  9. }
  10. service HelloGRPC{
  11. rpc SayHi(Request) returns (Response);
  12. }

二 . 入门—官方

首先,在本机上准备好例子代码的运行环境。

  1. 安装 Git
  2. 安装 grpc,获取源码
    1. $ go get -u github.com/grpc/grpc-go/examples/helloworld/greeter_client
    2. $ go get -u github.com/grpc/grpc-go/examples/helloworld/greeter_server
    切换当前目录到 examples/helloworld
    3. 定义服务

    创建我们例子的第一步是定义一个服务:一个 RPC 服务通过参数和返回类型来指定可以远程调用的方法。就像你在 概览 里所看到的, gRPC 通过 protocol buffers 来实现。 我们使用 protocol buffers 接口定义语言来定义服务方法,用 protocol buffer 来定义参数和返回类型。客户端和服务端均使用服务定义生成的接口代码。

这里有我们服务定义的例子,在 helloworld.proto 里用 protocol buffers IDL 定义的。Greeter 服务有一个方法 SayHello ,可以让服务端从远程客户端接收一个包含用户名的 HelloRequest 消息后,在一个 HelloReply 里发送回一个 Greeter。这是你可以在 gRPC 里指定的最简单的 RPC - 你可以在教程里找到针对你选择的语言更多类型的例子。

  1. syntax = "proto3";
  2. option java_package = "io.grpc.examples";
  3. package helloworld;
  4. // The greeter service definition.
  5. service Greeter {
  6. // Sends a greeting
  7. rpc SayHello (HelloRequest) returns (HelloReply) {}
  8. }
  9. // The request message containing the user's name.
  10. message HelloRequest {
  11. string name = 1;
  12. }
  13. // The response message containing the greetings
  14. message HelloReply {
  15. string message = 1;
  16. }
  1. 生成grpc代码

一旦定义好服务,我们可以使用 protocol buffer 编译器 protoc 来生成创建应用所需的特定客户端和服务端的代码 - 你可以生成任意 gRPC 支持的语言的代码,当然 PHP 和 Objective-C 仅支持创建客户端代码。生成的代码同时包括客户端的存根和服务端要实现的抽象接口,均包含 Greeter 所定义的方法。

(假如你没有在系统里安装 gRPC 插件和 protoc ,并且仅仅是要看一下这个例子,你可以跳过这一步,直接到下一步来查看生成的代码。)

为了生成客户端和服务端接口,运行 protocol buffer 编译器:

  1. protoc -I ../protos ../protos/helloworld.proto --go_out=plugins=grpchelloworld

这生成了 helloworld.pb.go ,包含了我们生成的客户端和服务端类,此外还有用于填充、序列化、提取 HelloRequest 和 HelloResponse 消息类型的类。

  1. 写一个服务器

    现在让我们写点代码!首先我们将创建一个服务应用来实现服务(你会记起来,我们可以是使用除了Objective-C and PHP 外的其他所有语言来实现)。在本节,我们不打算对如何创建一个服务端进行更深入地探讨 —— 更详细的信息可以在你选择语言对应的教程里找到。

服务端:
greeter_server/main.go 实现了 Greeter 服务所需要的行为。
正如你所见,服务器有一个 server 结构。它通过实现 sayHello 方法,实现了从 proto 服务定义生成的GreeterServer 接口:

  1. // server is used to implement helloworld.GreeterServer.
  2. type server struct{}
  3. // SayHello implements helloworld.GreeterServer
  4. func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
  5. return &pb.HelloReply{Message "Hello " + in.Name}, nil
  6. }

为了返回给客户端应答并且完成调用:

  1. 用我们的激动人心的消息构建并填充一个在我们接口定义的 HelloReply 应答对象。
  2. 将 HelloReply 返回给客户端。

  3. 服务端实现

需要提供一个 gRPC 服务的另一个主要功能是让这个服务实在在网络上可用。

greeter_server/main.go 提供了以下代码作为 Go 的例子。

  1. const (
  2. port = ":50051"
  3. )
  4. ...
  5. func main() {
  6. lis, err := net.Listen("tcp", port)
  7. if err != nil {
  8. log.Fatalf("failed to listen: %v", err)
  9. }
  10. s := grpc.NewServer()
  11. pb.RegisterGreeterServer(s, &server{})
  12. s.Serve(lis)
  13. }

客户端

客户端的 gRPC 非常简单。在这一步,我们将用生成的代码写一个简单的客户程序来访问我们在上一节里创建的 Greeter 服务器。 同样,我们也不打算对如何实现一个客户端程序深入更多,我们把这些内容放到教程里。

连接服务
首先我们看一下我们如何连接 Greeter 服务器。我们需要创建一个 gRPC 频道,指定我们要连接的主机名和服务器端口。然后我们用这个频道创建存根实例。

  1. const (
  2. address = "localhost:50051"
  3. defaultName = "world"
  4. )
  5. func main() {
  6. // Set up a connection to the server.
  7. conn, err := grpc.Dial(address)
  8. if err != nil {
  9. log.Fatalf("did not connect: %v", err)
  10. }
  11. defer conn.Close()
  12. c := pb.NewGreeterClient(conn)
  13. ...
  14. }

在 gRPC Go 你是使用一个特殊的 Dial() 方法来创建频道。

调用grpc
现在我们可以联系服务并获得一个 greeting :

  1. 我们创建并填充一个 HelloRequest 发送给服务。
  2. 我们用请求调用存根的 SayHello(),如果 RPC 成功,会得到一个填充的 HelloReply ,从其中我们可以获得 greeting。

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

    你可以在 greeter_client/main.go 里查看完整的客户端代码。

试一下:
你可以尝试用同一个语言在客户端和服务端构建并运行例子。或者你可以尝试 gRPC 最有用的一个功能 - 不同的语言间的互操作性,即在不同的语言运行客户端和服务端。每个服务端和客户端使用从同一过 proto 文件生成的接口代码,则意味着任何 Greeter 客户端可以与任何 Greeter 服务端对话。
首先运行服务端:
你可以用如下命令到 examples/helloworld 下运行服务端:

  1. $ greeter_server &

然后运行客户端: 你可以从examples/helloworld目录下用如下命令运行客户端:

  1. $ greeter_client