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

但是,请注意,要执行该命令,我们需要安装教程的 **设置** 部分中提到的正确依赖项。

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

Protobuf class code: python/greeting_pb2.py
Protobuf gRPC code: 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** 文件中存在的 method/function 的实现,即在我们的例子中,是 **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 通信设置了一个 Channel。

  • 然后,我们使用 channel 创建一个 **stub**。在这里,我们使用计划调用的函数的服务“**Greeter**”。stub 只不过是一个包装器,它隐藏了远程调用对调用者的复杂性。

  • 然后,我们简单地创建 proto 文件中定义的预期输入,即在我们的例子中,是 **ClientInput**。我们硬编码了两个参数,即 **name** 和 **greeting**。

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

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

客户端服务器调用

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

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

python .\server.py

输出

我们将得到以下输出 -

gRPC starting

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

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

python .\client.py

我们将看到以下输出 -

输出

Greeter client received following from server: Yo John!

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

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

因此,正如我们所看到的,客户端能够按预期调用服务器,并且服务器通过向客户端回送问候来响应。

广告