gRPC - 使用 Python 的 Hello World 应用程序

现在让我们创建一个基本的"Hello World"类应用程序,它将使用 gRPC 和 Python。

.proto 文件

首先让我们在 common_proto_files 中定义 greeting.proto 文件 −

syntax = "proto3";

service Greeter {
   rpc greet (ClientInput) returns (ServerOutput) {}
}
message ClientInput {
   string greeting = 1;
   string name = 2;
}
message ServerOutput {
   string message = 1;
}

现在让我们仔细看看上面块 − 中的每一行

syntax = "proto3";

这里的 "syntax" 代表我们正在使用的 Protobuf 版本。因此,我们使用最新版本 3,因此模式可以使用对版本 3 有效的所有语法。

package tutorial;

这里的 package 用于解决冲突,例如,我们有多个同名的类/成员。

service Greeter {
   rpc greet(ClientInput) returns (ServerOutput) {}
}

此块表示服务的名称"Greeter"和可调用的函数名称"greet""greet"函数接受类型为"ClientInput"的输入并返回类型为"ServerOutput"的输出。现在让我们看看这些类型。

message ClientInput {
   string greeting = 1;
   string name = 2;
}

在上面的代码块中,我们定义了 ClientInput,它包含两个属性,即"greeting"和"name",它们都是字符串。客户端应该将"ClientInput"类型的对象发送到服务器。

message ServerOutput {
   string message = 1;
}

这里,我们还定义了,给定一个"ClientInput",服务器将返回具有单个属性"message"的"ServerOutput"。服务器应该将类型为"ServerOutput"的对象发送到客户端。

现在,让我们为 Protobuf 类和 gRPC 类生成底层代码。为此,我们需要执行以下命令 −

python -m grpc_tools.protoc -I ..\common_proto_files\ --
python_out=../python --grpc_python_out=. Greeting.proto

但是,请注意,要执行该命令,我们需要安装正确的依赖项,如本教程的 setup 部分所述。

这应该会自动生成我们使用 gRPC 所需的源代码。源代码将放在 −

Protobuf 类代码:python/greeting_pb2.py
Protobuf gRPC 代码:python/greeting_pb2_grpcpb2.py

设置 gRPC 服务器

现在我们已经定义了包含函数定义的 proto 文件,让我们设置一个可以调用这些函数的服务器。

让我们编写服务器代码来提供上述功能并将其保存在 server.py

示例

from concurrent import futures

import grpc
import greeting_pb2
import greeting_pb2_grpc

class Greeter(greeting_pb2_grpc.GreeterServicer):
   def greet(self, request, context):
      print("Got request " + str(request))
      return greeting_pb2.ServerOutput(message='{0} {1}!'.format(request.greeting, request.name))
	  
def server():
   server = grpc.server(futures.ThreadPoolExecutor(max_workers=2))
   greeting_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
   server.add_insecure_port('[::]:50051')
   print("gRPC starting")
   server.start()
   server.wait_for_termination()
server()

上述代码在指定端口启动一个 gRPC 服务器,并提供我们在 proto 文件中编写的功能和服务。让我们看一下上面的代码 −

  • main 方法开始,我们在指定端口创建一个 gRPC 服务器。

  • 但在启动服务器之前,我们为服务器分配了我们想要运行的服务,即在我们的例子中为 Greeter 服务。

  • 为此,我们需要将服务实例传递给服务器,因此我们继续创建一个服务实例,即在我们的例子中为 Greeter

  • 服务实例需要提供 .proto 文件中存在的方法/函数的实现,即在我们的例子中为 greet 方法。

  • 该方法需要一个在 .proto 中定义的类型的对象文件,即我们的 request

  • 该方法对上述输入进行操作,进行计算,然后应该返回 .proto 文件中提到的输出,即我们的例子中的 ServerOutput

设置 gRPC 客户端

现在我们已经编写了服务器代码,让我们设置一个可以调用这些函数的客户端。

让我们编写客户端代码来调用上述函数并将其保存在 client.py

示例

import grpc

import greeting_pb2
import greeting_pb2_grpc

def run():
   with grpc.insecure_channel('localhost:50051') as channel:
      stub = greeting_pb2_grpc.GreeterStub(channel)
      response = stub.greet(greeting_pb2.ClientInput(name='John', greeting = "Yo"))
   print("Greeter client received following from server: " + response.message)   
run()

上述代码在指定端口启动 gRPC 服务器,并提供我们在 proto 文件中编写的功能和服务。让我们来看看上面的代码 −

  • main 方法开始,我们设置了一个用于与服务器进行 gRPC 通信的通道。

  • 然后,我们使用该通道创建一个 stub。在这里我们使用服务"Greeter",我们计划调用其功能。stub 只不过是一个包装器,它向调用者隐藏了远程调用的复杂性。

  • 然后,我们只需创建 proto 文件中定义的预期输入,即我们例子中的 ClientInput。我们已经硬编码了两个参数,即 namegreeting

  • 我们最终进行调用并等待服务器的结果。

所以,这就是我们的客户端代码。

客户端服务器调用

现在,我们已经定义了 proto 文件,编写了服务器和客户端代码,让我们继续执行此代码并查看实际操作。

要运行代码,请启动两个 shell。通过执行以下命令在第一个 shell 上启动服务器 −

python .\server.py

输出

我们将得到以下输出 −

gRPC 正在启动

上述输出表示服务器已启动。

现在,让我们启动客户端。

python .\client.py

我们将看到以下输出 −

输出

Greeter 客户端从服务器收到以下内容:Yo John!

现在,如果我们打开服务器日志,我们将看到以下数据 −

gRPC starting
Got request greeting: "Yo"
name: "John"

因此,正如我们所见,客户端能够按预期调用服务器,并且服务器响应并向客户端问候。