什么是rpc?grpc又是什么?
什么是RPC
远程过程调用协议RPC (Remote Procedure Call Protocol)
RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据
举例:在 a服务内,调用b服务的ticket服务中的get方法,那么在a服务里,直接调用b.ticket.get()来直接调用
客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。
什么是grpc
gRPC(Google Remote Procedure Call)是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf (Protocol Buffers)序列化协议开发,且支持众多开发语言(python,golang,javascript,C,C++、Node.js、Ruby、Objective-C、PHP和C#等)。
gRPC提供了一种简单的方法来精确地定义服务和为iOS、Android和后台支持服务自动生成可靠性很强的客户端功能库。客户端充分利用高级流和链接功能,从而有助于节省带宽、降低的TCP链接次数、节省CPU使用、和电池寿命。
RESTful | gRPC | |
---|---|---|
Full Name | REpresentational State Transfer | Google Remote Procedure Call |
Payload | Readable Data (typically JSON) | Unreadable Binary Data (Protobuf) |
HTTP | HTTP 1.1/HTTP 2 | HTTP/2 |
Performance | Slower | Faster |
Type Safety | No | Yes |
Cross Language | Yes | Yes |
Client Setup | Required | Not required |
Supported Functions | GET/PUT/DELETE/POST/... | Any function |
// Client端
// Student student = Call(ServerAddr, addAge, student)
1. 将这个调用映射为Call ID。
2. 将Call ID,student(params)序列化,以二进制形式打包
3. 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
4. 等待服务器返回结果
5. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新
// Server端
1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap
2. 等待客户端请求
3. 得到一个请求后,将其数据包反序列化,得到Call ID
4. 通过在callIdMap中查找,得到相应的函数指针
5. 将student(params)反序列化后,在本地调用addAge()函数,得到结果
6. 将student结果序列化后通过网络返回给Client
什么是protobuf
protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍
具有跨语言性:python golang java c++ javascript等等等等
生成python客户端和服务器
安装依赖
安装依赖:
pip install grpcio
pip install grpcio-tools
pip install protobuf
转化proto文件成client和server
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. wenet.proto
// 文件名 hello.proto,proto版本
syntax = "proto3";
package hello;
// 定义服务
service Greeter {
// 发送数据
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 请求消息,包含用户的姓名。
message HelloRequest {
string name = 1;
string age = 2;
string note=3;
}
// 响应消息,包含问候语。
message HelloReply {
string message = 1;
}
在命令行输入:
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. hello.proto
gRPC客户端(client.py):
import grpc # 导入gRPC库
import hello_pb2 # 导入生成的proto文件中的消息类型
import hello_pb2_grpc # 导入生成的proto文件中的服务类型
def run():
channel = grpc.insecure_channel('localhost:50051') # 创建与服务器的通信通道,参数为服务器的地址和端口
stub = hello_pb2_grpc.GreeterStub(channel) # 创建与服务器的Stub对象,参数为通信通道
response = stub.SayHello(hello_pb2.HelloRequest(name='John', age='25', note='Hello gRPC')) # 调用服务器上的方法,参数为请求消息对象
print("Response received from server: " + response.message) # 打印服务器返回的响应消息
if __name__ == '__main__':
run() # 运行客户端代码
gRPC服务器(server.py):
import grpc # 导入gRPC库
from concurrent import futures # 导入concurrent.futures库,用于多线程处理
import hello_pb2 # 导入生成的proto文件中的消息类型
import hello_pb2_grpc # 导入生成的proto文件中的服务类型
class GreeterServicer(hello_pb2_grpc.GreeterServicer):
def SayHello(self, request, context): # 实现proto文件中定义的方法,参数为请求消息对象和上下文对象
message = 'Hello, ' + request.name + '! You are ' + request.age + ' years old.' # 构造响应消息
return hello_pb2.HelloReply(message=message) # 返回响应消息对象
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) # 创建gRPC服务器对象,参数为线程池的最大工作线程数
hello_pb2_grpc.add_GreeterServicer_to_server(GreeterServicer(), server) # 将服务实现添加到服务器中,参数为服务实现对象和服务器对象
server.add_insecure_port('[::]:50051') # 指定服务器监听的地址和端口,参数为地址和端口
server.start() # 启动服务器
print("Server started. Listening on port 50051.") # 打印服务器启动信息
server.wait_for_termination() # 等待服务器终止
if __name__ == '__main__':
serve() # 运行服务器代码
常用protobuf数据类型和pb文件
常用protobuf数据类型
.proto Type | Notes | C++ Type | Java Type | Python Type[2] | Go Type | Ruby Type |
---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | |
float | float | float | float | float32 | Float | |
int32 | 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 | int32 | int | int | int32 | Fixnum 或者 Bignum(根据需要) |
uint32 | 使用变长编码 | uint32 | int | int | uint32 | Fixnum 或者 Bignum(根据需要) |
uint64 | 使用变长编码 | uint64 | long | int | uint64 | Bignum |
sint32 | 使用变长编码,这些编码在负值时比int32高效的多 | int32 | int | int | int32 | Fixnum 或者 Bignum(根据需要) |
sint64 | 使用变长编码,有符号的整型值。编码时比通常的int64高效。 | int64 | long | int | int64 | Bignum |
fixed32 | 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 | uint32 | int | int | uint32 | Fixnum 或者 Bignum(根据需要) |
fixed64 | 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 | uint64 | long | int | uint64 | Bignum |
sfixed32 | 总是4个字节 | int32 | int | int | int32 | Fixnum 或者 Bignum(根据需要) |
sfixed64 | 总是8个字节 | int64 | long | int | int64 | Bignum |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | |
string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 | string | String | str/unicode | string | String (UTF-8) |
bytes | 可能包含任意顺序的字节数据。 | string | ByteString | str/b'' | []byte | String (ASCII-8BIT) |
repeated | 数组(列表) | RepeatedField<type> | list | []type | RepeatedField |
map | 字典 | Map<key_type, value_type> | dict | {} | Map |
参考资料:grpc-python02--了解rpc与grpc以及创建一个简单的grpc-python_哔哩哔哩_bilibili
gRPC(一)入门:什么是RPC? | Go 技术论坛
https://www.youtube.com/watch?v=gnchfOojMk4
python grpc框架之一 简单示例_51CTO博客_python web 框架
Python gRPC 入门 - 掘金