> 技术文档 > Python + gRPC 演示:构建与实践指南

Python + gRPC 演示:构建与实践指南

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:gRPC是一个跨语言的高性能RPC框架,它基于HTTP/2协议,采用Protocol Buffers序列化协议。本文将介绍如何在Python中使用gRPC实现远程服务调用。通过定义服务接口、生成代码、服务实现、服务器启动、客户端交互以及拦截器的使用,我们将学习构建和使用gRPC服务的完整流程。演示项目包含在“python-grpc-demo-master”中,可作为实践参考,帮助理解gRPC在Python中的应用和分布式系统构建。
python-grpc-demo:Python + gRPC演示

1. gRPC框架概述

gRPC框架简介

gRPC是一个高性能、开源和通用的RPC框架,由Google主导开发。它允许客户端和服务端跨语言地进行通信,并且支持HTTP/2协议。gRPC使用Protocol Buffers作为接口定义语言(IDL),通过定义服务接口和方法来实现客户端和服务端之间的通信。gRPC适合于微服务架构,为构建分布式系统提供了简单和高效的方式。

gRPC的设计理念

gRPC的核心设计原则是强调高效的通信性能和跨平台的语言无关性。它将服务接口定义与实现细节分离,便于开发者专注于业务逻辑的构建而不必担心底层通信机制。gRPC支持四种基本的服务方法类型:一元RPC、服务器流式RPC、客户端流式RPC和双向流式RPC。这样的设计不仅提高了通信的灵活性,还增强了系统的可扩展性和互操作性。

gRPC的核心特性

  • 语言无关性 :gRPC支持多种语言,允许不同语言编写的服务之间无缝通信。
  • HTTP/2支持 :gRPC基于HTTP/2协议,利用其多路复用和头部压缩等特性来提高通信效率。
  • Protocol Buffers :作为gRPC的IDL,它能够提供紧凑的二进制格式,减少数据传输量。
  • 强大的跨语言支持 :gRPC通过Protocol Buffers的IDL,能够自动生成多种语言的客户端和服务端代码。
  • 四种服务方法类型 :提供灵活的通信模式,以适应不同的应用场景和需求。

gRPC与其他RPC框架的对比

gRPC与传统的RPC框架如Apache Thrift、Avro RPC相比,其最显著的优势在于对HTTP/2的内置支持以及更强大的跨语言能力。与RESTful API相比,gRPC在处理复杂数据交互和流式传输方面更为高效和简便。尽管如此,gRPC对于某些特定场景下的学习曲线较陡,而且对于简单的客户端和服务端交互,传统的HTTP API可能更为直观和容易理解。选择哪种RPC框架取决于项目需求、团队技能和生态系统的考量。

请注意,本章作为引入部分,旨在为读者提供gRPC框架的整体轮廓。随后的章节将深入探讨gRPC框架的各个方面,为读者提供更详细的实施和优化指南。

2. Protocol Buffers 数据序列化协议

2.1 Protocol Buffers基础

2.1.1 数据序列化与反序列化

在分布式系统和微服务架构中,数据序列化与反序列化是构建跨平台通信的关键技术之一。Protocol Buffers(简称 Protobuf)是Google开发的一种数据序列化协议,它通过 .proto 文件定义数据结构,然后使用Protobuf编译器生成特定语言的源代码,从而将结构化数据序列化为二进制格式,并能够还原为原始结构。

序列化(Serialization)是将对象状态转换为可以存储或传输的形式的过程。在Protobuf中,这涉及到将对象的数据成员转换为二进制数据,这些二进制数据可以存储到文件中或者通过网络传输。反序列化(Deserialization)则是将二进制数据恢复为原始对象的过程。

使用Protobuf的优势之一是其二进制格式比传统的文本格式(如JSON或XML)更加紧凑,传输速度更快,解析也更加高效。此外,Protobuf提供了跨语言的支持,这意味着同一个 .proto 文件可以用来生成不同编程语言的数据结构和访问函数,便于实现语言无关的通信。

2.1.2 Protocol Buffers的优势和应用场景

Protocol Buffers的主要优势在于其高效的性能、跨语言的兼容性以及易于维护的 .proto 定义文件。这些优势使得Protobuf特别适用于需要高效率序列化的场景,例如移动应用、微服务间通信、网络数据交换等。

在这些场景中,高效意味着较低的网络带宽消耗、快速的数据处理能力以及较小的内存占用。由于Protobuf是二进制格式,它比文本格式如JSON或XML提供了更好的性能,这在大规模数据处理和实时系统中尤为重要。

跨语言兼容性使得开发团队能够选择最适合特定任务的编程语言,而无需担心语言间的数据交换问题。这一特性在拥有多种服务端语言的大型系统中尤其有用。

维护 .proto 文件也比维护大量的手动序列化/反序列化代码要简单许多。 .proto 文件的结构清晰,易于阅读和理解,便于团队协作和代码维护。

2.2 .proto 文件结构和规则

2.2.1 .proto 文件的基本语法

.proto 文件定义了数据结构和RPC服务接口,是Protocol Buffers的核心。每个 .proto 文件由一系列的声明组成,这些声明定义了消息类型(message)和RPC服务接口(service)。

一个基本的 .proto 文件包含以下元素:
- 包声明(package)
- 消息类型定义(message)
- 字段定义(field)
- 服务接口定义(service)
- RPC方法声明(rpc)

下面是一个简单的 .proto 文件示例:

syntax = \"proto3\"; // 指定语法版本为proto3package example; // 定义包名,用于防止命名冲突// 定义一个消息message Person { string name = 1; // 字段定义,字段号为1,类型为string int32 id = 2; // 字段定义,字段号为2,类型为int32 string email = 3;}// 定义一个服务service ExampleService { // 定义一个RPC方法 rpc GetPerson (Person) returns (Person); }

在这个文件中,我们定义了一个 Person 消息类型和一个 ExampleService 服务,服务中包含一个名为 GetPerson 的RPC方法。使用 proto3 语法,字段默认规则为可选,并且消息字段必须是唯一的。

2.2.2 数据类型的定义和使用

.proto 文件中,数据类型是构建消息的基础。Protocol Buffers提供了丰富的数据类型供定义消息字段,包括基本类型、复合类型、枚举类型等。

下面列出了一些常用的数据类型:

  • double : 双精度浮点数
  • float : 单精度浮点数
  • int32 : 32位整数
  • int64 : 64位整数
  • uint32 : 32位无符号整数
  • uint64 : 64位无符号整数
  • bool : 布尔值
  • string : UTF-8编码的字符串
  • bytes : 字节序列

复合类型可以嵌套定义消息,创建更加复杂的数据结构。比如,可以在一个 Person 消息中嵌入另一个 Address 消息。

此外,Protobuf还支持枚举类型,可以让字段有固定可选的值集,这样可以保证字段值的有效性并简化代码。

2.2.3 服务定义语法和规则

.proto 文件中,除了可以定义消息类型,还可以定义RPC服务接口。在gRPC框架中,这些服务定义允许你声明远程可调用的方法,gRPC使用这些定义自动生成客户端和服务器端的代码。

在定义服务时,可以指定输入和输出消息类型,然后用 rpc 关键字来声明服务的方法。每个RPC方法映射到一个特定的gRPC端点,并定义了客户端如何调用该方法。

服务方法可以有四种类型:

  • rpc MethodName (RequestType) returns (ResponseType);
  • rpc MethodName (RequestType) returns (stream ResponseType);
  • rpc MethodName (stream RequestType) returns (ResponseType);
  • rpc MethodName (stream RequestType) returns (stream ResponseType);

这些方法定义了请求和响应是否是单个消息还是消息流。例如,一个单向消息流可以用来处理类似文件上传的场景,而双向消息流可以用于聊天应用或实时数据处理。

2.3 数据类型的高级特性

2.3.1 枚举类型和映射类型

.proto 文件中使用枚举类型可以限制消息字段的可能值。枚举值必须是唯一的,可以在全局范围内或者在特定消息内定义。

message MyMessage { enum MyEnum { UNKNOWN = 0; FIRST_VALUE = 1; SECOND_VALUE = 2; } MyEnum enum_field = 1;}

在这个例子中,我们定义了一个名为 MyEnum 的枚举类型,并在 MyMessage 消息中使用了 enum_field 字段,该字段必须是 MyEnum 中定义的值之一。

映射类型(Map)是Protocol Buffers中一种方便的数据结构,用于存储键值对映射。在映射字段的定义中,键可以是任何整数或字符串类型,而值可以是任何非映射类型。

message MyMessage { map my_map = 1;}

在这个例子中,我们定义了一个名为 my_map 的映射字段,其中键是字符串类型,值是32位整数。

2.3.2 默认值和字段规则

在Protocol Buffers中,每个字段都有一个默认值。对于数值类型,默认值是0;对于字符串类型,默认值是空字符串;对于布尔类型,默认值是false。对于消息字段,默认值是消息的“空”实例。当消息被解析时,未明确设置的字段将使用这些默认值。

字段规则定义了字段在消息中的行为,Protobuf支持以下字段规则:

  • required : 必须明确设置此字段,否则消息将不合法。
  • optional : 字段可以设置也可以不设置,未设置时使用默认值。
  • repeated : 字段可以重复多次,通常用于数组或列表。

在proto3语法中, optional 关键字被移除,因为其使用带来了一致性问题。现在,所有的字段都是 optional ,并且在被省略时会使用默认值。

对于 repeated 字段,它们是动态编码的,因此可以高效地处理数组或列表数据。由于 repeated 字段可以出现多次,因此没有默认值。

message MyMessage { int32 optional_field = 1; // proto3中无需明确设置required或optional repeated string repeated_field = 2;}

在这里, optional_field 可以是任何整数值或默认值0,而 repeated_field 可以包含零个或多个字符串值。

字段规则和默认值是定义消息和确保其完整性的重要工具。它们提供了灵活性和控制,使得Protobuf能够应对各种数据交换的需求。

3. gRPC服务定义与 .proto 文件

3.1 gRPC服务的定义

3.1.1 gRPC服务接口与方法定义

gRPC使用 .proto 文件定义服务接口和方法,其语法结构清晰,能够详细描述服务的功能以及需要传递的参数类型。定义一个gRPC服务包括命名服务以及在服务中声明RPC方法,每个方法都包括请求和响应消息。这里是一个简单的例子:

service Greeter { rpc SayHello(HelloRequest) returns (HelloReply);}message HelloRequest { string name = 1;}message HelloReply { string message = 1;}

在这个例子中,我们定义了一个名为 Greeter 的服务,它包含一个 SayHello 方法,该方法接受一个 HelloRequest 类型的消息作为请求,并返回一个 HelloReply 类型的消息作为响应。 name 字段在 HelloRequest 消息中定义,并且使用了字段编号 1

3.1.2 服务端流式RPC和客户端流式RPC

gRPC支持四种类型的RPC方法:简单RPC、服务端流式RPC、客户端流式RPC和双向流式RPC。流式RPC允许在单一RPC调用中发送和接收多个消息。

  • 服务端流式RPC 允许服务端发送一个消息流给客户端。客户端发出一次请求后,可以持续接收到服务端发送的多个响应。这在服务端有多个相关数据需要发送给客户端时非常有用。

  • 客户端流式RPC 则允许客户端发送一个消息流给服务端,而服务端在接收到所有请求后,返回单个响应。例如,在一个聊天应用中,客户端可以持续发送消息到服务端,服务端再返回处理结果。

3.1.3 双向流式RPC定义

双向流式RPC 允许多次请求和响应,在客户端和服务端之间形成双向消息流。这种类型的RPC可以用来实现如视频会议这样复杂的通信场景。

双向流式RPC在 .proto 文件中的定义方法如下:

service ChatService { rpc Chat(stream ChatMessage) returns (stream ChatMessage);}message ChatMessage { string content = 1; string sender = 2;}

在这个例子中,客户端和服务端可以交替地发送和接收 ChatMessage 消息。

3.2 .proto 文件的编写和管理

3.2.1 编写规范和最佳实践

为了保持 .proto 文件的清晰和可维护性,需要遵循一些编写规范和最佳实践:

  • 组织结构 :合理地组织 .proto 文件中的定义,例如将相关类型和服务放在同一个文件中。
  • 重用消息类型 :使用 import 语句来重用其他 .proto 文件中定义的消息类型。
  • 版本控制 :保持向前和向后兼容性,例如通过增加字段编号而不删除或修改现有字段。
  • 命名约定 :使用驼峰命名法对服务、消息和字段进行命名。

3.2.2 .proto 文件的版本管理和兼容性

随着服务的不断迭代, .proto 文件可能会发生变化。在变更过程中,遵循以下兼容性原则是非常重要的:

  • 向后兼容 :添加新字段时,必须使用新的字段编号,并且新字段在客户端中可以被忽略。
  • 向前兼容 :删除字段时,应将其注释掉而不是完全移除,并且可以通过设置字段为已弃用来通知客户端。
  • 标记弃用 :对于将来计划删除的字段,应标记为弃用,以此向开发者发出警告。

3.2.3 工具集成与自动化生成代码

自动化工具可以极大的提高开发效率,尤其是在gRPC服务的开发中,可以自动生成客户端和服务端的代码。以下是几种常用的工具:

  • protoc :官方的Protocol Buffers编译器,用于生成客户端和服务端的代码。
  • protoc-gen-grpc :gRPC编译器插件,用于生成gRPC相关的代码。
  • Swagger :API文档和测试工具,可以集成gRPC并自动生成API文档。

代码块

使用 protoc 生成gRPC服务的代码

protoc --proto_path=src --python_out=dst --grpc_python_out=dst your_proto_file.proto

参数说明

  • --proto_path=src :指定 .proto 文件所在的目录。
  • --python_out=dst :指定生成Python代码的输出目录。
  • --grpc_python_out=dst :指定生成gRPC服务代码的输出目录。
  • your_proto_file.proto :要处理的 .proto 文件名。

逻辑分析

以上命令会解析指定的 .proto 文件,并生成相应的Python代码和gRPC服务代码,代码会包含服务接口、客户端和服务端的基本实现,开发者可以在这些代码的基础上进行进一步的开发。

通过使用自动化工具和遵循最佳实践,开发者可以更高效地管理 .proto 文件,快速生成和更新代码库,从而专注于业务逻辑的实现。这不仅加快了开发过程,也确保了服务的稳定性和扩展性。

4. 从 .proto 生成Python代码

4.1 Protobuf编译器的工作原理

4.1.1 编译器的输入和输出

Protocol Buffers编译器( protoc )是一个命令行工具,它接受 .proto 文件作为输入,并根据这些文件定义的数据结构生成源代码。编译器能够生成多种语言的代码,包括但不限于C++, Java, Python等。在生成Python代码的情况下, protoc 还能够生成用于gRPC的代码,这些代码能够处理远程过程调用(RPC)。

输出包括:

  • 数据访问类:为在 .proto 文件中定义的每种消息类型生成一个数据访问类。
  • 服务接口代码:如果 .proto 文件中定义了gRPC服务, protoc 会为这些服务生成接口代码。
  • gRPC代理代码:为客户端和服务端生成用于RPC调用的代理代码。

4.1.2 编译器的使用方法和命令行选项

protoc 的使用非常直观。其基本命令格式如下:

protoc [options] 

其中 是需要编译的 .proto 文件路径列表, [options] 是可选的命令行参数,如 --python_out 用于指定Python代码生成的目标目录。

一些有用的 protoc 选项包括:

  • --python_out=OUT_DIR :指定生成Python代码的输出目录。
  • --grpc_python_out=OUT_DIR :在指定目录下生成gRPC服务相关的Python代码。
  • --proto_path=IMPORT_PATH :指定解析导入依赖的目录。如果未指定,则使用当前目录。

4.2 Python代码的自动生成

4.2.1 安装和使用 protoc 编译器插件

要生成Python代码,首先需要确保 protoc 和对应语言的插件已经安装。对于Python,需要安装 protoc-gen-python 插件,这通常可以通过Python包管理器 pip 来安装:

pip install protobuf

安装完成后,可以在 protoc 命令中使用该插件:

protoc --python_out=. --grpc_python_out=. your_service.proto

4.2.2 Python代码生成工具和插件的配置

在一些项目中,可能需要对代码生成进行自定义配置,比如修改生成类的包名或别名。这可以通过插件提供的配置选项来完成。

使用 --plugin 参数指定 protoc 的插件路径,然后传递配置参数:

protoc --plugin=protoc-gen-python=/path/to/plugin --python_out=. --grpc_python_out=. your_service.proto

4.2.3 自动生成代码的结构和内容

自动生成的代码包含了数据访问类和gRPC代理类。数据访问类包括:

  • 构造器(构造对象)
  • 字段访问器(getters)
  • 字段设置器(setters)
  • __str__() 方法(方便调试)

对于gRPC服务, protoc 还会生成服务接口以及服务代理类。这些代理类包括客户端实现和服务器实现。服务器实现会调用用户提供的实现方法,而客户端实现则负责与服务器通信。

4.3 代码生成后的自定义和扩展

4.3.1 代码自定义的策略和方法

生成的代码已经具备了基本的结构,但往往需要进一步的定制才能满足特定的需求。自定义策略可能包括:

  • 更改数据访问类的属性名称(在 .proto 文件中使用 option (custom_option).name = \"new_name\"; )。
  • 添加自定义的方法到数据访问类。
  • 为服务实现添加日志记录、错误处理逻辑等。

4.3.2 集成自定义代码到项目中

完成自定义后,需要将这些代码集成到项目的其他部分。这可能包括:

  • 更新项目代码以调用新的数据访问方法。
  • 配置服务器代码以使用新的服务实现。
  • 更新测试用例以验证自定义逻辑。

在集成自定义代码时,务必要保持代码的一致性和可维护性。建议将自动生成的代码放在一个单独的包中,以区分手写的代码,这样可以在下次生成代码时避免不必要的冲突。

5. Python中实现gRPC服务接口

5.1 gRPC服务端的实现流程

实现gRPC服务接口的第一步是创建一个能够响应远程方法调用的服务端。以下是实现gRPC服务端的基本步骤:

5.1.1 gRPC服务器的创建和启动

在Python中,首先需要安装gRPC和对应的protobuf编译器。可以使用pip来安装:

pip install grpciopip install grpcio-tools

之后,生成 .proto 文件对应的Python代码,如上一章节所述。

现在,我们将实现一个服务端:

import grpcfrom concurrent import futuresimport helloworld_pb2import helloworld_pb2_grpcclass Greeter(helloworld_pb2_grpc.GreeterServicer): def SayHello(self, request, context): return helloworld_pb2.HelloReply(message=\'Hello, %s!\' % request.name)def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) server.add_insecure_port(\'[::]:50051\') server.start() server.wait_for_termination()if __name__ == \'__main__\': serve()

这个简单的gRPC服务器实现了 Greeter 服务,并监听50051端口。

5.1.2 定义和实现服务端的逻辑

在上面的例子中,我们定义了一个 Greeter 服务,并实现了 SayHello 方法。这个方法接收客户端发送的请求,并返回一个简单的欢迎消息。

服务端实现的核心在于继承 GreeterServicer 类,并重写其中的 SayHello 方法。这个方法的参数和返回值都由对应的 .proto 文件定义。

5.1.3 服务端的错误处理和日志记录

错误处理可以通过 context 参数来实现。我们可以检查请求是否有效,或者在处理请求时抛出异常。而日志记录通常通过Python的 logging 模块来实现。

import logginglogging.basicConfig()def SayHello(self, request, context): try: # 正常的业务逻辑 return helloworld_pb2.HelloReply(message=\'Hello, %s!\' % request.name) except Exception as e: context.set_code(grpc.StatusCode.INTERNAL) context.set_details(\'Something went wrong\') logging.error(\"An error occurred: %s\", e) raise

通过使用 context.set_code context.set_details 方法,我们能够设置gRPC的响应状态码和详细信息,这对于调试和故障排除非常有用。

5.2 gRPC客户端的实现流程

客户端与服务端进行通信,调用服务端的远程方法。以下是客户端的基本实现步骤:

5.2.1 gRPC客户端的创建和连接

在Python中创建一个简单的gRPC客户端:

import grpcfrom helloworld_pb2 import HelloReplyfrom helloworld_pb2_grpc import GreeterStubdef run(): with grpc.insecure_channel(\'localhost:50051\') as channel: stub = GreeterStub(channel) response = stub.SayHello(helloworld_pb2.HelloRequest(name=\'world\')) print(\"Greeter client received: \" + response.message)if __name__ == \'__main__\': run()

5.2.2 调用远程服务的方法和处理响应

客户端通过创建 GreeterStub 的实例来调用远程服务。 GreeterStub 的构造函数接受一个gRPC通道作为参数,并且可以使用 SayHello 方法发起远程调用。

response = stub.SayHello(helloworld_pb2.HelloRequest(name=\'Python\'))

客户端接收服务端的响应,并进行相应处理。

5.2.3 客户端的错误处理和异步调用

与服务端类似,客户端也需要进行错误处理。在gRPC中,通常情况下,如果服务端没有成功处理请求,调用将会抛出异常。客户端可以捕获这些异常进行处理。

try: response = stub.SayHello(helloworld_pb2.HelloRequest(name=\'Python\'))except grpc.RpcError as e: print(\'RPC failed: %s\' % e)

对于异步调用,可以使用 grpc.aio 模块来实现,这允许客户端异步地发起调用并继续执行,直到响应到达。

import grpc.aioasync def run(): async with grpc.aio.insecure_channel(\'localhost:50051\') as channel: stub = GreeterStub(channel) response = await stub.SayHello(helloworld_pb2.HelloRequest(name=\'Python\')) print(\"Greeter client received: \" + response.message)if __name__ == \'__main__\': import asyncio asyncio.run(run())

在异步客户端中,使用 async await 关键字来处理异步调用。

5.3 gRPC服务的测试和部署

测试和部署是任何服务生命周期中不可忽视的部分。gRPC服务的测试和部署通常包括单元测试、集成测试和打包。

5.3.1 单元测试和集成测试的编写

单元测试通常针对单个方法或模块。在Python中,可以使用 unittest 模块来编写单元测试:

import unittestfrom helloworld_pb2 import HelloReplyfrom helloworld_pb2_grpc import add_GreeterServicer_to_server, GreeterServicerclass TestGreeterServicer(GreeterServicer): def SayHello(self, request, context): return HelloReply(message=\'Hello, %s!\' % request.name)class GreeterServicerTest(unittest.TestCase): def test_say_hello(self): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) add_GreeterServicer_to_server(TestGreeterServicer(), server) server.add_insecure_port(\'[::]:50051\') server.start() with grpc.insecure_channel(\'localhost:50051\') as channel: stub = GreeterStub(channel) response = stub.SayHello(helloworld_pb2.HelloRequest(name=\'test\')) self.assertEqual(response.message, \'Hello, test!\') server.stop(None)if __name__ == \'__main__\': unittest.main()

集成测试则需要运行整个服务端,并确保客户端能够与之正确通信。

5.3.2 gRPC服务的打包和部署策略

在Python中,可以使用 setuptools 来打包gRPC服务,创建一个包含所有依赖的分发包。

from setuptools import setup, find_packagessetup( name=\'greeter\', version=\'0.1\', packages=find_packages(), install_requires=[ \'grpcio\', \'grpcio-tools\', ], # 其他安装参数...)

服务部署可以使用Docker容器来实现,这允许服务在不同环境之间快速迁移和部署。

5.3.3 容器化技术在gRPC服务部署中的应用

下面是一个简单的Dockerfile示例,它将gRPC服务打包为Docker镜像:

# 使用Python官方镜像作为基础镜像FROM python:3.8# 将依赖文件复制到容器中并安装COPY ./requirements.txt /app/requirements.txtWORKDIR /appRUN pip install -r requirements.txt# 将源代码复制到容器中COPY . /app# 启动服务CMD [\"python\", \"your_server.py\"]

使用 docker build 来创建镜像,并使用 docker run 来运行服务。

以上是关于如何在Python中实现gRPC服务接口的一系列步骤和细节。无论是在服务端还是客户端,gRPC都提供了强大的工具和库来简化分布式系统的开发过程。而容器化技术为服务部署带来了灵活性和扩展性,使得gRPC服务的维护和扩展更加简便。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:gRPC是一个跨语言的高性能RPC框架,它基于HTTP/2协议,采用Protocol Buffers序列化协议。本文将介绍如何在Python中使用gRPC实现远程服务调用。通过定义服务接口、生成代码、服务实现、服务器启动、客户端交互以及拦截器的使用,我们将学习构建和使用gRPC服务的完整流程。演示项目包含在“python-grpc-demo-master”中,可作为实践参考,帮助理解gRPC在Python中的应用和分布式系统构建。

本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif