AI原生应用微服务通信:REST vs gRPC对比
在当今的AI原生应用开发中,微服务架构越来越受欢迎。微服务之间的通信方式对于整个应用的性能、可维护性等方面有着至关重要的影响。本文的目的就是详细对比REST和gRPC这两种常见的微服务通信方式,让开发者了解它们的优缺点,从而在实际开发中做出更合适的选择。范围涵盖了这两种通信方式的核心概念、原理、代码实现以及实际应用场景等方面。本文首先会介绍一些相关的术语,然后通过有趣的故事引入REST和gRPC的
AI原生应用微服务通信:REST vs gRPC对比
关键词:AI原生应用、微服务通信、REST、gRPC、对比
摘要:本文聚焦于AI原生应用中微服务通信的两种重要方式——REST和gRPC,深入探讨它们的特点、原理及适用场景。通过通俗易懂的语言和生动的例子,像给小学生讲故事一样,将复杂的技术概念讲解清楚。详细对比了REST和gRPC在多个方面的差异,帮助读者理解在不同情况下应如何选择合适的通信方式,以提升AI原生应用的性能和开发效率。
背景介绍
目的和范围
在当今的AI原生应用开发中,微服务架构越来越受欢迎。微服务之间的通信方式对于整个应用的性能、可维护性等方面有着至关重要的影响。本文的目的就是详细对比REST和gRPC这两种常见的微服务通信方式,让开发者了解它们的优缺点,从而在实际开发中做出更合适的选择。范围涵盖了这两种通信方式的核心概念、原理、代码实现以及实际应用场景等方面。
预期读者
本文主要面向对AI原生应用开发和微服务通信感兴趣的开发者、技术爱好者以及相关领域的学生。无论你是刚刚接触微服务开发的新手,还是有一定经验的开发者,都能从本文中获得有价值的信息。
文档结构概述
本文首先会介绍一些相关的术语,然后通过有趣的故事引入REST和gRPC的核心概念,解释它们的含义以及它们之间的关系。接着会阐述它们的核心算法原理和具体操作步骤,给出数学模型和公式,并通过项目实战展示代码的实际案例。之后会探讨它们的实际应用场景,推荐一些相关的工具和资源。最后会总结所学内容,提出一些思考题,并提供常见问题的解答和扩展阅读的参考资料。
术语表
核心术语定义
- AI原生应用:是指那些从设计之初就充分考虑并利用人工智能技术的应用程序,它们依赖于AI算法和模型来实现核心功能。
- 微服务:一种将单个应用程序拆分成多个小型、自治服务的架构风格,每个服务都可以独立开发、部署和扩展。
- REST:Representational State Transfer的缩写,是一种基于HTTP协议的软件架构风格,用于设计网络应用程序的接口。
- gRPC:是一种高性能、开源的远程过程调用(RPC)框架,由Google开发。
相关概念解释
- 远程过程调用(RPC):允许程序调用另一个地址空间(通常是在另一台计算机上)的过程或函数,而不需要显式编写远程交互的代码。就好像在本地调用函数一样简单。
- HTTP协议:超文本传输协议,是用于在互联网上传输超文本的协议,是REST通信的基础。
缩略词列表
- REST:Representational State Transfer
- RPC:Remote Procedure Call
- gRPC:gRPC Remote Procedure Call
核心概念与联系
故事引入
想象一下,有一个大型的魔法王国,这个王国里有很多不同的魔法工坊。每个工坊都有自己独特的魔法技能,比如有的工坊擅长制作魔法药水,有的工坊擅长打造魔法武器。为了让这些工坊之间能够相互协作,完成更强大的魔法任务,就需要一种通信方式。
有一种通信方式就像是在王国里设立了很多公共的信使站,每个工坊可以把自己的需求写在一封信里,然后通过信使站把信送到其他工坊。信使站会根据信上的地址,把信准确地送到目的地。这种方式就有点像REST通信。
还有一种通信方式,就像是每个工坊都有一根神奇的魔法管道,当一个工坊需要另一个工坊的帮助时,只要通过这根管道喊一声,对方就能立刻收到信息并做出回应。这种方式就类似于gRPC通信。
核心概念解释(像给小学生讲故事一样)
核心概念一:REST
REST就像是我们去超市购物。超市里有很多不同的货架,每个货架上都放着不同的商品。我们可以通过不同的路径找到我们想要的商品。比如,我们要找苹果,就可以去水果区的苹果货架。在REST中,这些货架就相当于资源,每个资源都有一个唯一的地址(URL)。我们可以通过HTTP方法(比如GET、POST、PUT、DELETE)来对这些资源进行操作。就像我们去超市买苹果,用GET方法就是看看苹果有多少、什么价格;用POST方法就是把苹果放到购物车里;用PUT方法就是修改苹果的数量;用DELETE方法就是把苹果从购物车里拿出来。
核心概念二:gRPC
gRPC就像是打电话。当我们给朋友打电话时,我们可以直接告诉朋友我们的需求,然后朋友会根据我们的需求做出回应。在gRPC中,我们定义了一些服务和方法,就像我们和朋友之间约定好的交流内容。当一个服务需要调用另一个服务的方法时,就像我们打电话给朋友提出请求一样,对方会根据这个请求执行相应的操作并返回结果。
核心概念三:微服务通信
微服务通信就像是一个班级里的同学们一起完成一个大项目。每个同学都有自己擅长的部分,比如有的同学擅长画画,有的同学擅长写作。为了完成这个项目,同学们需要相互交流,分享自己的想法和成果。在微服务架构中,每个微服务就像班级里的同学,它们需要通过某种方式进行通信,才能共同完成整个应用的功能。
核心概念之间的关系(用小学生能理解的比喻)
概念一和概念二的关系:REST和gRPC就像两种不同的交通方式。REST就像是坐公交车,它是一种比较通用的方式,大家都知道怎么坐,而且可以在很多地方使用。但是公交车可能会比较慢,而且有时候会很拥挤。gRPC就像是坐高铁,它速度很快,效率很高,但是它的建设成本比较高,而且不是在所有地方都有高铁线路。在微服务通信中,REST适用于对性能要求不是特别高,但是需要通用性和兼容性的场景;gRPC适用于对性能要求很高,需要快速响应的场景。
概念二和概念三的关系:gRPC就像是班级里的快速传话员。当一个同学有紧急的消息需要告诉另一个同学时,快速传话员可以迅速把消息传递过去。在微服务通信中,gRPC可以帮助微服务之间快速地传递信息,提高整个系统的响应速度。
概念一和概念三的关系:REST就像是班级里的公告栏。每个同学都可以在公告栏上发布自己的信息,也可以从公告栏上获取其他同学的信息。在微服务通信中,REST可以让微服务之间通过一种通用的方式进行信息的交流和共享。
核心概念原理和架构的文本示意图
REST
REST架构基于客户端 - 服务器模型。客户端通过HTTP协议向服务器发送请求,请求中包含请求方法(GET、POST等)、请求的资源地址(URL)和请求的数据(如果有)。服务器接收到请求后,根据请求的内容进行相应的处理,并返回响应给客户端。响应中包含响应状态码(如200表示成功,404表示未找到资源等)和响应数据。
gRPC
gRPC基于Protocol Buffers(一种序列化数据的方式)和HTTP/2协议。客户端和服务器通过定义的服务和方法进行通信。客户端调用服务器的方法时,会将请求数据序列化为Protocol Buffers格式,通过HTTP/2协议发送给服务器。服务器接收到请求后,将数据反序列化,执行相应的方法,并将结果序列化为Protocol Buffers格式返回给客户端。
Mermaid 流程图
核心算法原理 & 具体操作步骤
REST
核心算法原理
REST的核心是基于HTTP协议的资源操作。客户端通过不同的HTTP方法对服务器上的资源进行增删改查操作。例如,GET方法用于获取资源,POST方法用于创建资源,PUT方法用于更新资源,DELETE方法用于删除资源。
具体操作步骤
以下是一个使用Python的Flask框架实现简单REST API的示例:
from flask import Flask, jsonify, request
app = Flask(__name__)
# 模拟一个数据库
data = []
# 获取所有资源
@app.route('/resources', methods=['GET'])
def get_resources():
return jsonify(data)
# 创建一个资源
@app.route('/resources', methods=['POST'])
def create_resource():
new_resource = request.get_json()
data.append(new_resource)
return jsonify(new_resource), 201
# 获取单个资源
@app.route('/resources/<int:resource_id>', methods=['GET'])
def get_resource(resource_id):
if resource_id < len(data):
return jsonify(data[resource_id])
return jsonify({'message': 'Resource not found'}), 404
# 更新单个资源
@app.route('/resources/<int:resource_id>', methods=['PUT'])
def update_resource(resource_id):
if resource_id < len(data):
updated_resource = request.get_json()
data[resource_id] = updated_resource
return jsonify(updated_resource)
return jsonify({'message': 'Resource not found'}), 404
# 删除单个资源
@app.route('/resources/<int:resource_id>', methods=['DELETE'])
def delete_resource(resource_id):
if resource_id < len(data):
del data[resource_id]
return jsonify({'message': 'Resource deleted'})
return jsonify({'message': 'Resource not found'}), 404
if __name__ == '__main__':
app.run(debug=True)
代码解释
- 首先,我们导入了Flask框架和相关的模块。
- 然后,创建了一个Flask应用实例。
- 接着,定义了一些路由和对应的处理函数,用于实现对资源的增删改查操作。
- 最后,启动了Flask应用。
gRPC
核心算法原理
gRPC基于Protocol Buffers进行数据序列化和反序列化。Protocol Buffers是一种高效的数据序列化方式,它可以将数据结构转换为二进制格式,从而减少数据传输的大小。gRPC使用HTTP/2协议进行通信,HTTP/2协议具有多路复用、二进制分帧等特性,提高了通信的效率。
具体操作步骤
以下是一个使用Python和gRPC实现简单服务的示例:
- 定义服务和消息类型(
example.proto
):
syntax = "proto3";
package example;
// 定义请求消息
message Request {
string name = 1;
}
// 定义响应消息
message Response {
string greeting = 1;
}
// 定义服务
service ExampleService {
// 定义方法
rpc SayHello (Request) returns (Response);
}
- 生成代码:
使用protoc
工具生成Python代码:
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. example.proto
- 实现服务器端代码(
server.py
):
import grpc
from concurrent import futures
import example_pb2
import example_pb2_grpc
class ExampleService(example_pb2_grpc.ExampleServiceServicer):
def SayHello(self, request, context):
return example_pb2.Response(greeting=f'Hello, {request.name}!')
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
example_pb2_grpc.add_ExampleServiceServicer_to_server(ExampleService(), server)
server.add_insecure_port('[::]:50051')
server.start()
print("Server started, listening on port 50051")
server.wait_for_termination()
if __name__ == '__main__':
serve()
- 实现客户端代码(
client.py
):
import grpc
import example_pb2
import example_pb2_grpc
def run():
channel = grpc.insecure_channel('localhost:50051')
stub = example_pb2_grpc.ExampleServiceStub(channel)
response = stub.SayHello(example_pb2.Request(name='World'))
print("Greeter client received: " + response.greeting)
if __name__ == '__main__':
run()
代码解释
- 首先,我们使用Protocol Buffers定义了服务和消息类型。
- 然后,使用
protoc
工具生成了Python代码。 - 接着,实现了服务器端代码,定义了服务的具体逻辑。
- 最后,实现了客户端代码,调用服务器的方法并获取响应。
数学模型和公式 & 详细讲解 & 举例说明
REST
在REST中,主要涉及到的是HTTP协议的状态码和响应时间。HTTP状态码是一个三位的数字,用于表示请求的结果。常见的状态码有:
- 200200200:表示请求成功。
- 404404404:表示请求的资源未找到。
- 500500500:表示服务器内部错误。
响应时间可以用以下公式表示:
Tresponse=Tnetwork+TserverT_{response} = T_{network} + T_{server}Tresponse=Tnetwork+Tserver
其中,TresponseT_{response}Tresponse 是总的响应时间,TnetworkT_{network}Tnetwork 是网络传输时间,TserverT_{server}Tserver 是服务器处理时间。
例如,当我们使用GET方法请求一个资源时,如果服务器处理时间为 0.50.50.5 秒,网络传输时间为 0.10.10.1 秒,那么总的响应时间就是 0.5+0.1=0.60.5 + 0.1 = 0.60.5+0.1=0.6 秒。
gRPC
在gRPC中,主要涉及到数据序列化和反序列化的时间。数据序列化时间可以用以下公式表示:
Tserialize=Tdata×SrateT_{serialize} = T_{data} \times S_{rate}Tserialize=Tdata×Srate
其中,TserializeT_{serialize}Tserialize 是数据序列化时间,TdataT_{data}Tdata 是数据的大小,SrateS_{rate}Srate 是序列化速率。
反序列化时间的公式类似:
Tdeserialize=Tdata×DrateT_{deserialize} = T_{data} \times D_{rate}Tdeserialize=Tdata×Drate
其中,TdeserializeT_{deserialize}Tdeserialize 是数据反序列化时间,DrateD_{rate}Drate 是反序列化速率。
例如,当我们有一个大小为 101010 KB的数据,序列化速率为 111 KB/ms,反序列化速率为 222 KB/ms,那么序列化时间就是 10÷1=1010 \div 1 = 1010÷1=10 ms,反序列化时间就是 10÷2=510 \div 2 = 510÷2=5 ms。
项目实战:代码实际案例和详细解释说明
开发环境搭建
REST
- 安装Python(建议使用Python 3.7及以上版本)。
- 安装Flask框架:
pip install flask
gRPC
- 安装Python(建议使用Python 3.7及以上版本)。
- 安装
grpcio
和grpcio-tools
:
pip install grpcio grpcio-tools
- 安装
protobuf
:
pip install protobuf
源代码详细实现和代码解读
REST
我们在前面已经给出了一个使用Flask框架实现简单REST API的示例,这里再详细解读一下代码。
from flask import Flask, jsonify, request
app = Flask(__name__)
# 模拟一个数据库
data = []
# 获取所有资源
@app.route('/resources', methods=['GET'])
def get_resources():
return jsonify(data)
# 创建一个资源
@app.route('/resources', methods=['POST'])
def create_resource():
new_resource = request.get_json()
data.append(new_resource)
return jsonify(new_resource), 201
# 获取单个资源
@app.route('/resources/<int:resource_id>', methods=['GET'])
def get_resource(resource_id):
if resource_id < len(data):
return jsonify(data[resource_id])
return jsonify({'message': 'Resource not found'}), 404
# 更新单个资源
@app.route('/resources/<int:resource_id>', methods=['PUT'])
def update_resource(resource_id):
if resource_id < len(data):
updated_resource = request.get_json()
data[resource_id] = updated_resource
return jsonify(updated_resource)
return jsonify({'message': 'Resource not found'}), 404
# 删除单个资源
@app.route('/resources/<int:resource_id>', methods=['DELETE'])
def delete_resource(resource_id):
if resource_id < len(data):
del data[resource_id]
return jsonify({'message': 'Resource deleted'})
return jsonify({'message': 'Resource not found'}), 404
if __name__ == '__main__':
app.run(debug=True)
from flask import Flask, jsonify, request
:导入Flask框架和相关的模块。app = Flask(__name__)
:创建一个Flask应用实例。data = []
:模拟一个数据库,用于存储资源。@app.route('/resources', methods=['GET'])
:定义一个路由,当客户端使用GET方法请求/resources
时,调用get_resources
函数。def get_resources()
:返回所有资源。@app.route('/resources', methods=['POST'])
:定义一个路由,当客户端使用POST方法请求/resources
时,调用create_resource
函数。def create_resource()
:从请求中获取新资源的数据,添加到data
列表中,并返回新资源。@app.route('/resources/<int:resource_id>', methods=['GET'])
:定义一个路由,当客户端使用GET方法请求/resources/<resource_id>
时,调用get_resource
函数。def get_resource(resource_id)
:根据资源的ID返回对应的资源,如果资源不存在,返回404错误。@app.route('/resources/<int:resource_id>', methods=['PUT'])
:定义一个路由,当客户端使用PUT方法请求/resources/<resource_id>
时,调用update_resource
函数。def update_resource(resource_id)
:根据资源的ID更新对应的资源,如果资源不存在,返回404错误。@app.route('/resources/<int:resource_id>', methods=['DELETE'])
:定义一个路由,当客户端使用DELETE方法请求/resources/<resource_id>
时,调用delete_resource
函数。def delete_resource(resource_id)
:根据资源的ID删除对应的资源,如果资源不存在,返回404错误。app.run(debug=True)
:启动Flask应用,并开启调试模式。
gRPC
我们在前面也给出了一个使用Python和gRPC实现简单服务的示例,这里再详细解读一下代码。
example.proto
:
syntax = "proto3";
package example;
// 定义请求消息
message Request {
string name = 1;
}
// 定义响应消息
message Response {
string greeting = 1;
}
// 定义服务
service ExampleService {
// 定义方法
rpc SayHello (Request) returns (Response);
}
syntax = "proto3";
:指定使用Protocol Buffers 3的语法。package example;
:定义包名。message Request
:定义请求消息,包含一个字符串类型的字段name
。message Response
:定义响应消息,包含一个字符串类型的字段greeting
。service ExampleService
:定义服务,包含一个方法SayHello
,该方法接受一个Request
类型的参数,返回一个Response
类型的结果。
server.py
:
import grpc
from concurrent import futures
import example_pb2
import example_pb2_grpc
class ExampleService(example_pb2_grpc.ExampleServiceServicer):
def SayHello(self, request, context):
return example_pb2.Response(greeting=f'Hello, {request.name}!')
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
example_pb2_grpc.add_ExampleServiceServicer_to_server(ExampleService(), server)
server.add_insecure_port('[::]:50051')
server.start()
print("Server started, listening on port 50051")
server.wait_for_termination()
if __name__ == '__main__':
serve()
import grpc
:导入gRPC库。from concurrent import futures
:导入线程池模块。import example_pb2
和import example_pb2_grpc
:导入生成的代码。class ExampleService(example_pb2_grpc.ExampleServiceServicer)
:定义服务的实现类。def SayHello(self, request, context)
:实现SayHello
方法,根据请求中的name
字段返回相应的问候语。server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
:创建一个gRPC服务器,并使用线程池处理请求。example_pb2_grpc.add_ExampleServiceServicer_to_server(ExampleService(), server)
:将服务的实现类添加到服务器中。server.add_insecure_port('[::]:50051')
:指定服务器监听的端口。server.start()
:启动服务器。server.wait_for_termination()
:等待服务器终止。
client.py
:
import grpc
import example_pb2
import example_pb2_grpc
def run():
channel = grpc.insecure_channel('localhost:50051')
stub = example_pb2_grpc.ExampleServiceStub(channel)
response = stub.SayHello(example_pb2.Request(name='World'))
print("Greeter client received: " + response.greeting)
if __name__ == '__main__':
run()
import grpc
:导入gRPC库。import example_pb2
和import example_pb2_grpc
:导入生成的代码。channel = grpc.insecure_channel('localhost:50051')
:创建一个不安全的gRPC通道,连接到服务器。stub = example_pb2_grpc.ExampleServiceStub(channel)
:创建服务的客户端存根。response = stub.SayHello(example_pb2.Request(name='World'))
:调用服务器的SayHello
方法,并传入请求参数。print("Greeter client received: " + response.greeting)
:打印服务器返回的响应。
代码解读与分析
REST
- 优点:
- 简单易懂,基于HTTP协议,开发和调试都比较方便。
- 通用性强,可以被各种客户端和服务器支持。
- 支持多种数据格式,如JSON、XML等。
- 缺点:
- 性能相对较低,因为HTTP协议的开销较大。
- 缺乏强类型约束,容易出现数据格式错误。
gRPC
- 优点:
- 性能高,使用Protocol Buffers进行数据序列化,减少了数据传输的大小。
- 强类型约束,通过Protocol Buffers定义服务和消息类型,减少了数据格式错误的可能性。
- 支持流式传输,可以实现高效的双向通信。
- 缺点:
- 学习成本较高,需要了解Protocol Buffers和gRPC的使用。
- 兼容性相对较差,不是所有的客户端和服务器都支持gRPC。
实际应用场景
REST
- Web应用开发:REST是Web应用开发中最常用的通信方式,因为它基于HTTP协议,与浏览器和Web服务器的兼容性很好。例如,开发一个电商网站,前端页面可以通过REST API与后端服务器进行通信,获取商品信息、提交订单等。
- 第三方集成:当需要与第三方服务进行集成时,REST API是一个很好的选择。因为大多数第三方服务都提供了REST API,通过REST API可以方便地与这些服务进行交互。例如,与支付网关、社交媒体平台等进行集成。
gRPC
- 微服务架构:在微服务架构中,各个微服务之间的通信对性能要求较高。gRPC的高性能和强类型约束使得它非常适合用于微服务之间的通信。例如,一个大型的电商系统可以拆分成多个微服务,如商品服务、订单服务、用户服务等,这些微服务之间可以使用gRPC进行通信。
- 实时数据传输:gRPC支持流式传输,可以实现高效的实时数据传输。例如,在物联网应用中,传感器设备需要实时将数据传输到服务器,使用gRPC可以提高数据传输的效率。
工具和资源推荐
REST
- Postman:一个强大的API开发和测试工具,可以方便地发送HTTP请求,测试REST API。
- Swagger:一个用于设计、构建、文档化和使用RESTful Web服务的工具集,可以自动生成API文档。
gRPC
- Protocol Buffers:官方的Protocol Buffers编译器和库,用于生成代码和进行数据序列化。
- gRPC Gateway:一个将gRPC服务转换为REST API的工具,方便与现有的REST客户端进行集成。
未来发展趋势与挑战
REST
- 趋势:随着Web技术的不断发展,REST仍然会是Web应用开发中主流的通信方式。同时,REST API的安全性和性能优化将成为未来的发展方向。
- 挑战:如何在保证通用性的前提下,提高REST API的性能和安全性是一个挑战。例如,如何减少HTTP协议的开销,如何防止API被恶意攻击等。
gRPC
- 趋势:随着微服务架构的普及和对性能要求的提高,gRPC的应用会越来越广泛。同时,gRPC与其他技术的集成也将成为未来的发展方向。例如,与云计算、人工智能等技术的集成。
- 挑战:如何降低gRPC的学习成本,提高其兼容性是一个挑战。例如,如何让更多的开发者快速上手gRPC,如何让gRPC与现有的系统更好地集成等。
总结:学到了什么?
核心概念回顾
- 我们学习了REST和gRPC这两种微服务通信方式。REST是一种基于HTTP协议的软件架构风格,通过不同的HTTP方法对服务器上的资源进行操作。gRPC是一种高性能、开源的远程过程调用框架,基于Protocol Buffers和HTTP/2协议进行通信。
- 我们还了解了微服务通信的概念,即微服务之间需要通过某种方式进行信息的交流和共享,以共同完成整个应用的功能。
概念关系回顾
- 我们了解了REST和gRPC之间的关系,它们就像两种不同的交通方式,各有优缺点。REST适用于对性能要求不是特别高,但是需要通用性和兼容性的场景;gRPC适用于对性能要求很高,需要快速响应的场景。
- 我们还了解了REST和gRPC与微服务通信的关系,它们都是微服务通信的重要方式,在不同的场景中发挥着不同的作用。
思考题:动动小脑筋
思考题一:你能想到生活中还有哪些地方用到了类似REST和gRPC的通信方式吗?
思考题二:如果你要开发一个大型的AI原生应用,你会如何选择REST和gRPC作为微服务通信方式?
附录:常见问题与解答
问题一:REST和gRPC可以同时使用吗?
答:可以。在一个大型的应用中,可以根据不同的场景选择使用REST和gRPC。例如,对于与外部系统的交互,可以使用REST API;对于内部微服务之间的通信,可以使用gRPC。
问题二:gRPC一定比REST性能好吗?
答:不一定。gRPC在数据序列化和传输效率上通常比REST高,但是在某些场景下,REST的性能也可以满足需求。例如,当数据量较小、对响应时间要求不高时,REST的简单性和通用性可能更重要。
问题三:如何选择合适的通信方式?
答:可以根据以下几个方面来选择合适的通信方式:
- 性能要求:如果对性能要求很高,需要快速响应,建议选择gRPC;如果对性能要求不是特别高,更注重通用性和兼容性,建议选择REST。
- 开发成本:REST的开发成本相对较低,因为它基于HTTP协议,开发和调试都比较方便;gRPC的学习成本较高,需要了解Protocol Buffers和gRPC的使用。
- 数据格式:REST支持多种数据格式,如JSON、XML等;gRPC使用Protocol Buffers进行数据序列化,数据格式相对固定。
扩展阅读 & 参考资料
- 《RESTful Web Services》
- 《gRPC: Up and Running》
- 官方文档:Flask、gRPC、Protocol Buffers等。
更多推荐
所有评论(0)